Kids Hobby Prediction Dataset
Problem
Children often find themselves at a crossroads when it comes to
discovering their passions or hobbies, be it in academics, arts, or
sports. Recognizing the importance of guiding children towards
activities they are passionate about; we have curated a dataset obtained
through surveys conducted with parents. This dataset compiles valuable
information about children’s preferences, enabling the creation of a
classification model aimed at predicting kids’ hobbies.
In this exploratory journey, we delve into the dataset to uncover
patterns and insights that can assist parents in understanding their
child’s inclinations better. Through the application of clustering
techniques, we aim to categorize children based on number of columns
(Fav_sub , Scholarship, etc..). The ultimate goal is to provide parents
with meaningful recommendations, fostering an environment where children
can thrive in activities that genuinely resonate with their
interests.
By analyzing this dataset, we hope to offer insightful information
that will aid parents in guiding their kids toward the right
hobbies.
Data Mining Task
The data mining task at hand revolves around predicting kids’ hobbies
based on a dataset named “Hobby_Data,” obtained through specific
questions posed to their parents regarding preferences, capabilities,
and achievements. This dataset serves as the foundation for two primary
data mining tasks: classification and clustering. In the classification
process, the goal is to train a machine learning model to be capable of
accurately predicting a child’s hobby as either “academic,” “art,” or
“sports.” This requires using the data collected from parents to
establish patterns and relationships that guide the model in making
accurate predictions. Concurrently, the clustering process involves
partitioning the dataset into meaningful clusters, grouping together
children with similar characteristics or preferences. Through these dual
approaches, the data mining task aims to develop a robust predictive
model capable of accurately categorizing children’s hobbies in order to
uncover the inherent structures, patterns, and associations within the
dataset, contributing to a deeper understanding of the diverse interests
and engagement levels of the young population in academic, artistic, and
sports-related activities.
Data
The source: https://www.kaggle.com/datasets/abtabm/hobby-prediction-basic
Number of objects: 1601
Number of attributes: 14
Description: Following the selection of our data set
(“Hobby_Data”) which predicts kids’ hobbies, that was collected by
asking their parents specific questions about their kid’s preferences,
capabilities, and achievements. To help us train the machine to predict
the kid’s hobby. We will begin to preprocess and analyze the data.
Attribute Description:
| Olympiad_Participation |
Has your child participated in any Science/Maths |
Boolean |
| Scholarship |
Has he/she received any scholarship? |
Boolean |
| School |
Love’s going to school? |
Boolean |
| Fav_sub |
What is his/her favorite subject? |
Categorical |
| Projects |
Has done any projects under academics before? |
Boolean |
| Grasp_pow |
His/Her Grasping power (1-6) |
Ordinal |
| Time_sprt |
How much time does he/she spend playing outdoor/indoor games? |
Ordinal |
| Medals |
Medals won in Sports? |
Boolean |
| Career_sprt |
Want’s to pursue his/her career in sports? |
Boolean |
| Act_sprt |
Regular in his/her sports activities? |
Boolean |
| Fant_arts |
Love creating fantasy paintings? |
Boolean |
| Won_arts |
Won art competitions? |
Ordinal |
| Time_art |
Time utilized in Arts? |
Ordinal |
| Predicted Hobby |
predictions for the hobby that the kid wouldl ike |
Categorical |
======= >>>>>>> Stashed changes
General information about the data set:
str(Hobby_Data)
sample(Hobby_Data)
variables distribution:
In our dataset, numeric variables are not available; instead, we have
three ordinal variables. Due to the nature of our data types, certain
types of graphs, such as scatter plots and box plots, were not suitable
for our analysis.
variables distribution of Time_sprt:
install.packages("magrittr") # install only one time then put this command as comment after installation
library(magrittr) ## for pipe operations
Hobby_Data$Time_art %>% density() %>% plot(main='variables distribution of Time_art')
In the “Time_art” variable, parents were requested to assess the time
their child dedicated to artistic pursuits like painting or paper
crafting, using a scale ranging from 1 to 6, where 6 represents the
highest level of involvement. It’s worth noting that the concentration
of lower ratings at the lower end of the scale (1) is quite pronounced,
and this tendency may be attributed to the inherent inclination of
children towards physical activities.
variables distribution of Time_art:
Hobby_Data$Time_sprt %>% density() %>% plot(main='variables distribution of Time_sprt')
Parents were requested to assess their children’s involvement in
sports on a scale from 1 to 6 within the “Time_sprt” variable. Notably,
the most prevalent ranking was 3, suggesting a moderate level of sports
participation. It’s interesting to observe that the distribution
exhibits a shape akin to a bell curve, indicating that a substantial
proportion of children have a genuine love for sports.
variables distribution of Grasp_pow:
Hobby_Data$Grasp_pow %>% density() %>% plot(main='variables distribution of Grasp_pow')
The density graph depicting parents’ ratings of their children’s
grasp power, which ranges from 1 to 6, illustrates a trend where the
most common rating is level 3, followed by level 4, level 5, level 2,
level 1, and level 6. This distribution indicates that a substantial
proportion of parents believe their children possess grasp power that is
average or slightly above average (levels 3 and 4), with fewer children
rated at the extremes (levels 1, 2, 5, and 6).
variables distribution of the class label ‘Predicted Hobby’:
install.packages("dplyr") # install only one time then put this command as comment after installation
library(dplyr)
dataset2 <- Hobby_Data %>% sample_n(1600)
table(dataset2$`Predicted Hobby`) %>% pie()
tab <- dataset2$`Predicted Hobby` %>% table()
precentages <- tab %>% prop.table() %>% round(3) * 100
txt <- paste0(names(tab), '\n', precentages, '%')
pie(tab, labels=txt)
The pie chart illustrates the distribution of the class label
‘Predicted Hobby’. It’s evident that a substantial portion,
approximately 43.7%, of the children’s hobbies are academic in nature,
indicating a strong interest in educational pursuits. Additionally,
30.8% of the kids are engaged in sports, reflecting a significant
inclination towards physical activities. Arts-related hobbies account
for 25.6% of the total, the distribution reflects a harmonious blend of
hobbies. This balanced distribution not only signifies a variety of
interests but also indicates a well-rounded engagement of children in
academic, physical, and creative activities.
Statistical measures :
find_mode <- function(x) {
u <- unique(x)
tab <- tabulate(match(x, u))
u[tab == max(tab)]
}
find_mode(Hobby_Data$Time_art)
# Updated upstream
hist(Hobby_Data$Time_art)
#Stashed changes
The histogram makes it evident that the mode is equal to 1, and we
believe that the high frequency of parents ranking “1” as the most
chosen rank in the “Time_art” variable could be attributed to several
factors. It might indicate that a significant number of parents perceive
their children’s involvement in art activities as relatively low,
possibly due to time constraints, academic priorities, or a limited
interest in art. Alternatively, it could reflect that parents value a
more balanced approach to their children’s activities, with a variety of
interests and responsibilities sharing their time. This trend could also
result from a cultural or educational emphasis on other subjects and
extracurricular activities that compete for a child’s time, potentially
leading to a lower ranking for art-related activities.
find_mode(Hobby_Data$Grasp_pow)
hist(Hobby_Data$Grasp_pow)
Rank 3, in this context, may have been the most chosen rank because
it likely represents an average or moderate level of grasp power.
Parents may have assessed their children’s grasp power as neither
exceptionally strong (rank 5 or 6) nor particularly weak (rank 1 or 2),
resulting in the preference for the middle-ranking option. This choice
could reflect a perception that their children’s grasp power falls
within a typical or expected range, making it the most common
rating.
find_mode(Hobby_Data$Time_sprt)
hist(Hobby_Data$Time_sprt)
As shown in the histogram the rank 3 on a scale of 1 to 6 was likely
the most chosen rank for assessing children’s involvement in sports
because it represents a balanced middle ground. Parents may have
perceived rank 3 as indicating that their children are moderately
involved in sports, not excessively committed or disinterested. This
middle-of-the-road ranking reflects a common perspective that many
parents may hold, considering that extreme rankings, such as 1 or 6,
might suggest either a lack of involvement or an excessive focus on
sports, which may not align with their perception of their child’s
overall well-rounded development.
The histogram graph for “Time_art” variable
hist(Hobby_Data$Time_art)
The histogram makes it evident that the mode is equal to 1, and we
believe that the high frequency of parents ranking “1” as the most
chosen rank in the “Time_art” variable could be attributed to several
factors. It might indicate that a significant number of parents perceive
their children’s involvement in art activities as relatively low,
possibly due to time constraints, academic priorities, or a limited
interest in art. Alternatively, it could reflect that parents value a
more balanced approach to their children’s activities, with a variety of
interests and responsibilities sharing their time. This trend could also
result from a cultural or educational emphasis on other subjects and
extracurricular activities that compete for a child’s time, potentially
leading to a lower ranking for art-related activities.
find_mode(Hobby_Data$Grasp_pow)
The histogram graph for “Grasp_pow” variable
hist(Hobby_Data$Grasp_pow)
Rank 3, in this context, may have been the most chosen rank because
it likely represents an average or moderate level of grasp power.
Parents may have assessed their children’s grasp power as neither
exceptionally strong (rank 5 or 6) nor particularly weak (rank 1 or 2),
resulting in the preference for the middle-ranking option. This choice
could reflect a perception that their children’s grasp power falls
within a typical or expected range, making it the most common
rating.
find_mode(Hobby_Data$Time_sprt)
The histogram graph for “Time_sprt” variable
hist(Hobby_Data$Time_sprt)
As shown in the histogram the rank 3 on a scale of 1 to 6 was likely
the most chosen rank for assessing children’s involvement in sports
because it represents a balanced middle ground. Parents may have
perceived rank 3 as indicating that their children are moderately
involved in sports, not excessively committed or disinterested. This
middle-of-the-road ranking reflects a common perspective that many
parents may hold, considering that extreme rankings, such as 1 or 6,
might suggest either a lack of involvement or an excessive focus on
sports, which may not align with their perception of their child’s
overall well-rounded development.
Data preprocessing
#1# Data cleaning:
During the data cleaning stage, finding and fixing faults,
inconsistencies, and errors in a dataset helps it be more reliable and
of higher quality for analysis and modeling. There are methods for
handling missing values, detecting outliers, resolving inconsistencies,
and standardizing formats.
import Dataset”Hobby_Data”
View(Hobby_Data)
str(Hobby_Data)
check missing value :
is.na(Hobby_Data)
find the total null values in the dataset:
sum(is.na(Hobby_Data))
Description:
This stage involves checking and deleting for null and missing values
because they might have a significant impact on the data and cause
errors and negative effects in subsequent steps.We simply looked for
missing values, and there are a missing values in our dataset. According
to our investigation, the dataset does not contain any outliers since it
doesn’t have a numerical data type. Additionally, there are no
inconsistent values or other errors.
Dataset after cleaning step:
sample(Hobby_Data)
#2#Encoding: This step includes a Converting
categorical or non-numeric data into a numerical format, which is
necessary for compatibility with subsequent steps in preprocessing.
Hobby_Data$Olympiad_Participation = factor(Hobby_Data$Olympiad_Participation,levels = c("No", "Yes"), labels = c(0, 1))
Hobby_Data$Scholarship = factor(Hobby_Data$Scholarship , levels = c("No", "Yes"), labels = c(0, 1))
Hobby_Data$School = factor(Hobby_Data$School, levels = c("No", "Yes"), labels = c(0, 1))
Hobby_Data$Projects = factor(Hobby_Data$Projects, levels = c("No", "Yes"), labels = c(0, 1))
Hobby_Data$Medals = factor(Hobby_Data$Medals, levels = c("No", "Yes"), labels = c(0, 1))
Hobby_Data$Career_sprt = factor(Hobby_Data$Career_sprt, levels = c("No", "Yes"), labels = c(0, 1))
Hobby_Data$Act_sprt = factor(Hobby_Data$Act_sprt, levels = c("No", "Yes"), labels = c(0, 1))
Hobby_Data$Fant_arts = factor(Hobby_Data$Fant_arts, levels = c("No", "Yes"), labels = c(0, 1))
Hobby_Data$Won_arts = factor(Hobby_Data$Won_arts, levels = c("No", "Maybe", "Yes"), labels = c(0, 2, 1))
Hobby_Data$Fav_sub = factor(Hobby_Data$Fav_sub, levels = c("Science", "Mathematics", "History/Geography", "Any language"), labels = c(1, 2, 3, 4))
Hobby_Data$`Predicted Hobby` <- factor(Hobby_Data$`Predicted Hobby`, levels = c("Academics", "Arts", "Sports"), labels = c(1, 2, 3))
Dataset after Encoding :
sample(Hobby_Data)
#3# Normalization and Discetization:
We don’t need to use normalization and discetization in our dataset.
Since our dataset doesn’t have numeric attributes and normalization
involves mathematical operations, which can result in meaningless values
and errors, Also, applying discretization leads to a loss of information
and creates intervals and relationships that don’t exist between
values.
#4#Feature Selection:
To improve the accuracy of our predictions for the target class
“Predicted Hobby” and decrease the processing time of our classifier, we
will utilize feature selection techniques. These techniques enable us to
eliminate redundant or irrelevant attributes from the dataset, resulting
in a more concise subset of features that provide the most valuable
information for our predictions.
Specifically, we will use two feature selection methods: Rank
Features by Importance and Feature Selection Using Recursive Feature
Elimination (RFE).
1.Rank Features by Importance:
This method used in the code helps us determine which features are
most important for predicting the “Predicted Hobby” class label in the
dataset. It utilizes the Random Forest algorithm, known for its accurate
prediction capabilities. This method calculates the importance of each
feature by assessing its contribution to the overall accuracy of the
predictions. By ranking the features based on their importance, we can
identify the ones that have the greatest influence on determining
hobbies.
Ensure the results are repeatable by setting a seed:
set.seed(7)
Load the necessary libraries:
install.packages("caret")
install.packages("randomForest")
library(caret)
library(randomForest)
Separate the predictors and the class label:
predictors <- Hobby_Data[, -14] # Excluding the class label (Predicted Hobby)
class_label <- Hobby_Data$`Predicted Hobby`
Train a Random Forest model:
model <- randomForest(predictors, class_label, importance = TRUE)
Get the variable importance:
importance <- importance(model)
Rank the features by importance:
ranked_features <- sort(importance[, "MeanDecreaseGini"], decreasing = TRUE)
Print the ranked features:
print(ranked_features)
barplot(ranked_features, horiz = TRUE, las = 1, main = "Kids Hobby Variable Importance Ranking")
2.Feature Selection Using RFE:
The Recursive Feature Elimination (RFE) method with Random Forest is
a technique used to select the most important features for accurate
predictions . It iteratively eliminates less relevant features,
retraining the model at each step to evaluate performance. By focusing
on the most informative features, RFE improves reduces complexity, and
enhances prediction accuracy. It is particularly effective with Random
Forest due to its ability to handle complex relationships and
high-dimensional data. RFE helps identify the most important attributes
associated with the target variable, enabling the creation of more
efficient and accurate models.
Ensure the results are repeatable by setting a seed:
set.seed(7)
Load the necessary libraries:
library(caret)
Define the control parameters for RFE using random forest selection
function:
control <- rfeControl(functions = rfFuncs, method = "cv", number = 10)
Extract the predictor variables from Hobby_Data:
predictors <- Hobby_Data[, -ncol(Hobby_Data)]
Convert the outcome variable to a factor:
outcome <- as.factor(Hobby_Data$`Predicted Hobby`)
Run the RFE algorithm:
results <- rfe(predictors, outcome, sizes = 1:ncol(Hobby_Data), rfeControl = control)
Summarize the results:
print(results)
List the chosen features selected by RFE:
predictors(results)
Plot the results:
plot(results, type = c("g", "o"))
3.Removing Irrelevant Columns:
By considering both Recursive Feature Elimination (RFE) and Rank By
Importance, we can make informed decisions about feature relevance and
impact on the model. In this case, the columns “School,” “Medals” should
be deleted as they have lower importance scores compared to the selected
variables. Removing these columns simplifies the model and reduces
dimensionality, eliminating potential noise and irrelevant information
that could hinder accurate predictions.
Remove the specified columns from the Hobby_Kids dataset:
Hobby_Data <- Hobby_Data[, !(colnames(Hobby_Data) %in% c("School", "Medals"))]
Display the updated dataset after deleting columns:
sample(Hobby_Data)
we can see that the two columns are deleted(“School”, “Medals”).
Balanced Data
# Calculate class imbalance
class_imbalance <- max(prop.table(table(Hobby_Data$`Predicted Hobby`))) - min(prop.table(table(Hobby_Data$`Predicted Hobby`)))
# Print the result
print(class_imbalance)
if class_imbalance is close to 0, it suggests that the proportions of
different classes are relatively similar, indicating a balanced dataset.
Conversely, if class_imbalance is larger, it suggests a more significant
imbalance between the classes. The calculated class imbalance value of
0.1805122 suggests that the distribution of classes in the
“Predicted_Hobby” column of the dataset is relatively balanced. The
class imbalance is a measure of the difference between the proportions
of the most prevalent and least prevalent classes. In this case, the
value is close to 0, indicating that there is a minimal difference
between the proportions of different classes. A lower-class imbalance
value is generally desirable, as it signifies a more even distribution
of instances across classes, which can be beneficial for the training
and performance of machine learning models.
Data Mining Technique
Evaluation And Comparison
—————————————————
Classification—————————————————-
After preprocessing, we will proceed to the classification step. In
this phase, as part of supervised learning, we will apply a
classification algorithm to assign each data point into predefined
categories based on its attributes. This involves selecting the most
relevant features that have been cleaned and formatted during
preprocessing. The selected model will then learn from training data,
enabling it to predict the category of new, unseen data accurately. This
step is essential for making informed decisions or predictions based on
the data.
We implement a decision tree on the dataset, which has been
partitioned into Training and Test sets using the percentage split
method. This method ensures that each subset is a randomized,
representative sample of the entire dataset, thus minimizing bias and
enabling consistent model performance evaluation. We choose three
different split sizes: (“90%”, “10%”), (“80%”, “20%”), and (“70%”,
“30%”). These varying sizes are selected to provide insight into the
model’s performance with different amounts of data, which is vital for
detecting unique patterns and confirming the model’s consistency in
various situations.
In the final steps, we utilize data visualization tools to create
visual representations of our decision trees. Additionally, we conduct
an exhaustive evaluation of the model, employing a “Confusion Matrix” to
illustrate the outcomes clearly.
Information Gain
Information Gain is particularly useful when dealing with categorical
target variables using Information Gain for the initial partitioning of
a dataset when building a decision tree It’s based on the concept of
entropy and aims to maximize the homogeneity of subsets after each
split. This approach helps create an effective decision tree by
selecting features that provide the most information for predicting the
target variable.
1-Information Gain(70%,30%)
# Load the required packages
library(rpart)
library(rpart.plot)
library(caret)
# Set the seed for reproducibility
set.seed(1234)
# Split the data into training and testing sets
ind <- sample(2, nrow(Hobby_Data), replace=TRUE, prob=c(0.7, 0.3))
trainData <- Hobby_Data[ind == 1,]
testData <- Hobby_Data[ind == 2,]
# Define the formula for the decision tree
myFormula <- `Predicted Hobby` ~ Scholarship + Fav_sub + Projects + Grasp_pow + Time_sprt + Career_sprt + Act_sprt + Fant_arts + Won_arts + Time_art + Olympiad_Participation
# Create the decision tree model with the "information" splitting criterion
Hobby_Data_ctree <- rpart(myFormula, data = trainData, method = "class", parms = list(split = "information"))
# Print the decision tree
print(Hobby_Data_ctree)
# Plot the decision tree
rpart.plot(Hobby_Data_ctree)
Decision Tree Analysis Using Information
gain(70/30):
In Frist Tree ,we devide dataset into training set and test set with
size(%70,%30) respectively. As you can see in the figure, the root node
(“Career_sprt”) serves as the starting point for the classification
process since have the heights Gain. The dataset has a distribution of
approximately 42.47% for class 1(“Academics”), 25.92% for class
2(“Arts”), and 31.61% for class 3 (“Sports”).
The tree further branches based on the values of the “Career_sprt” if
equal 0, tree branches based on”Won_arts” ,and majority of instances
fall into “Acadimcs”, constituting 63.93% , if”Won_arts” equal 0 or2 In
this case the tree terminates with a leaf node indicating a high
probability 89.08% for “Acadimcs”.else the instances are classified
based on “Fant_arts” into “Arts” with a probability 88%,if equal 0 the
instances are classified into “Academics” with a probability of 73%
,else the instances are classified into “Arts” with a high probability
96%, if “Career_sprt” is 1, the tree further branches based on the
“Fant_arts” into “Sports” with a probability 80%. If “Fant_arts” is 1 ,
there is another split based on the “Time_art” feature into “Arts” with
a probability 49%. If “Time_art” is greater than or equal to 3, the
instances are classified into “Arts” with a probability of 87% . On the
other hand, if “Time_art” is less than 3, the instances are classified
into “Sports” with a probability of 65% .If “Fant_arts” not equal 1 ,
the instances are classified into “Sports” with a high probability 95%,
as indicated by the leaf node.
First Confusion matrix
# Predict on the test data
testPred <- predict(Hobby_Data_ctree, newdata = testData, type = 'class')
# Check the accuracy of the model
accuracy <- sum(testPred == testData$`Predicted Hobby`) / nrow(testData) * 100
cat('Accuracy:', accuracy, '\n')
# Create a confusion matrix
conf_matrix <- table(Actual = testData$`Predicted Hobby`, Predicted = testPred)
# Calculate precision for each class
precision_class_1 <- conf_matrix[1, 1] / sum(conf_matrix[1, ])
precision_class_2 <- conf_matrix[2, 2] / sum(conf_matrix[2, ])
precision_class_3 <- conf_matrix[3, 3] / sum(conf_matrix[3, ])
# Calculate sensitivity for each class
sensitivity_class_1 <- conf_matrix[1, 1] / sum(conf_matrix[1, ])
sensitivity_class_2 <- conf_matrix[2, 2] / sum(conf_matrix[2, ])
sensitivity_class_3 <- conf_matrix[3, 3] / sum(conf_matrix[3, ])
# Calculate specificity for each class
specificity_class_1 <- sum(diag(conf_matrix[-1, -1])) / sum(conf_matrix[-1, ])
specificity_class_2 <- sum(conf_matrix[c(1, 3), c(1, 3)]) / sum(conf_matrix[c(1, 3), ])
specificity_class_3 <- sum(conf_matrix[c(1, 2), c(1, 2)]) / sum(conf_matrix[c(1, 2), ])
# Calculate macro-average sensitivity
macro_avg_sensitivity <- (sensitivity_class_1 + sensitivity_class_2 + sensitivity_class_3) / 3
# Calculate macro-average specificity
macro_avg_specificity <- (specificity_class_1 + specificity_class_2 + specificity_class_3) / 3
# Calculate macro-average precision
macro_avg_precision <- (precision_class_1 + precision_class_2 + precision_class_3) / 3
# Print macro-average sensitivity
cat('Average Sensitivity:', macro_avg_sensitivity, '\n')
# Print macro-average specificity
cat('Average Specificity:', macro_avg_specificity, '\n')
# Print macro-average precision
cat('Average Precision:', macro_avg_precision, '\n')
# Print precision for each class
cat('Precision for Class 1:', precision_class_1, ' \n')
cat('Precision for Class 2:', precision_class_2, ' \n')
cat('Precision for Class 3:', precision_class_3, ' \n')
# Print sensitivity for each class
cat('Sensitivity for Class 1:', sensitivity_class_1, '\n')
cat('Sensitivity for Class 2:', sensitivity_class_2, '\n')
cat('Sensitivity for Class 3:', sensitivity_class_3, '\n')
# Print specificity for each class
cat('Specificity for Class 1:', specificity_class_1, '\n')
cat('Specificity for Class 2:', specificity_class_2, '\n')
cat('Specificity for Class 3:', specificity_class_3, '\n')
2-Information Gain(80%,20%)
library(rpart)
library(caTools)
library(rpart.plot)
library(caret)
# Set the seed for reproducibility
set.seed(1234)
# Split the data into training and testing sets
ind <- sample(2, nrow(Hobby_Data), replace=TRUE, prob=c(0.8, 0.2))
trainData <- Hobby_Data[ind == 1,]
testData <- Hobby_Data[ind == 2,]
# Define the formula for the decision tree
myFormula <- `Predicted Hobby` ~ Scholarship + Fav_sub + Projects + Grasp_pow + Time_sprt + Career_sprt + Act_sprt + Fant_arts + Won_arts + Time_art + Olympiad_Participation
# Create the decision tree model with the "information" splitting criterion
Hobby_Data_ctree <- rpart(myFormula, data = trainData, method = "class", parms = list(split = "information"))
# Print the decision tree
print(Hobby_Data_ctree)
# Plot the decision tree
rpart.plot(Hobby_Data_ctree)
Decision Tree Analysis Using Information
gain(80/20):
In Second Tree ,we devide dataset into training set and test set with
size(%80,%20) respectively. As you can see in the figure, the root node
(“Career_sprt”) serves as the starting point for the classification
process since have the heights Gain. The dataset has a distribution of
approximately 43%for class1 (“Academics”), 26% for class 2(“Arts”), and
31% for class 3 (“Sports”).
The tree further branches based on the values of the “Career_sprt” if
equal 0, tree branches based on”Won_arts” ,and majority of instances
fall into “Acadimcs”, constituting 65% , if”Won_arts” equal 0 or2 In
this case the tree terminates with a leaf node indicating a high
probability 90% for “Acadimcs”.else the instances are classified based
on “Fant_arts”into “Arts” with a probability 88%,if equal 0 the
instances are classified into “Academics” with a probability of 72%
,else the instances are classified into “Arts” with a high probability
96%, if “Career_sprt” is 1, the tree further branches based on the
“Fant_arts” into “Sports” with a probability 78%. If “Fant_arts” is 1 ,
there is another split based on the “Time_art” into “Arts” with a
probability 50%. If “Time_art” is greater than or equal to 3, the
instances are classified into “Arts” with a probability of 85% . On the
other hand, if “Time_art” is less than 3, the instances are classified
into “Sports” with a probability of 58% .If “Fant_arts” If not equal 1 ,
the instances are classified into “Sports” with a high probability 94%,
as indicated by the leaf node.
Second Confusion matrix
# Predict on the test data
testPred <- predict(Hobby_Data_ctree, newdata = testData, type = 'class')
# Check the accuracy of the model
accuracy <- sum(testPred == testData$`Predicted Hobby`) / nrow(testData) * 100
cat('Accuracy:', accuracy, '\n')
# Create a confusion matrix
conf_matrix <- table(Actual = testData$`Predicted Hobby`, Predicted = testPred)
# Calculate precision for each class
precision_class_1 <- conf_matrix[1, 1] / sum(conf_matrix[1, ])
precision_class_2 <- conf_matrix[2, 2] / sum(conf_matrix[2, ])
precision_class_3 <- conf_matrix[3, 3] / sum(conf_matrix[3, ])
# Calculate sensitivity for each class
sensitivity_class_1 <- conf_matrix[1, 1] / sum(conf_matrix[1, ])
sensitivity_class_2 <- conf_matrix[2, 2] / sum(conf_matrix[2, ])
sensitivity_class_3 <- conf_matrix[3, 3] / sum(conf_matrix[3, ])
# Calculate specificity for each class
specificity_class_1 <- sum(diag(conf_matrix[-1, -1])) / sum(conf_matrix[-1, ])
specificity_class_2 <- sum(conf_matrix[c(1, 3), c(1, 3)]) / sum(conf_matrix[c(1, 3), ])
specificity_class_3 <- sum(conf_matrix[c(1, 2), c(1, 2)]) / sum(conf_matrix[c(1, 2), ])
# Calculate macro-average sensitivity
macro_avg_sensitivity <- (sensitivity_class_1 + sensitivity_class_2 + sensitivity_class_3) / 3
# Calculate macro-average specificity
macro_avg_specificity <- (specificity_class_1 + specificity_class_2 + specificity_class_3) / 3
# Calculate macro-average precision
macro_avg_precision <- (precision_class_1 + precision_class_2 + precision_class_3) / 3
# Print macro-average sensitivity
cat('Average Sensitivity:', macro_avg_sensitivity, '\n')
# Print macro-average specificity
cat('Average Specificity:', macro_avg_specificity, '\n')
# Print macro-average precision
cat('Average Precision:', macro_avg_precision, '\n')
# Print precision for each class
cat('Precision for Class 1:', precision_class_1, ' \n')
cat('Precision for Class 2:', precision_class_2, ' \n')
cat('Precision for Class 3:', precision_class_3, ' \n')
# Print sensitivity for each class
cat('Sensitivity for Class 1:', sensitivity_class_1, '\n')
cat('Sensitivity for Class 2:', sensitivity_class_2, '\n')
cat('Sensitivity for Class 3:', sensitivity_class_3, '\n')
# Print specificity for each class
cat('Specificity for Class 1:', specificity_class_1, '\n')
cat('Specificity for Class 2:', specificity_class_2, '\n')
cat('Specificity for Class 3:', specificity_class_3, '\n')
3-Information Gain(90%,10%)
library(rpart)
library(caTools)
library(rpart.plot)
library(caret)
# Set the seed for reproducibility
set.seed(1234)
# Split the data into training and testing sets
ind <- sample(2, nrow(Hobby_Data), replace=TRUE, prob=c(0.9, 0.1))
trainData <- Hobby_Data[ind == 1,]
testData <- Hobby_Data[ind == 2,]
# Define the formula for the decision tree
myFormula <- `Predicted Hobby` ~ Scholarship + Fav_sub + Projects + Grasp_pow + Time_sprt + Career_sprt + Act_sprt + Fant_arts + Won_arts + Time_art + Olympiad_Participation
# Create the decision tree model with the "information" splitting criterion
Hobby_Data_ctree <- rpart(myFormula, data = trainData, method = "class", parms = list(split = "information"))
# Print the decision tree
print(Hobby_Data_ctree)
# Plot the decision tree
rpart.plot(Hobby_Data_ctree)
Decision Tree Analysis Using Information
gain(90/10):
In Third Tree ,we devide dataset into training set and test set with
size(%90,%10) respectively. As you can see in the figure, the root node
(“Career_sprt”) serves as the starting point for the classification
process since have the heights Gain. The dataset has a distribution of
approximately 43% for class 1(“Academics”), 25% for class 2(“Arts”), and
32% for class 3 (“Sports”).
The tree further branches based on the values of the “Career_sprt” if
equal 0, tree branches based on”Won_arts” ,and majority of instances
fall into “Acadimcs”, constituting 65% , if”Won_arts” equal 0 or 2 In
this case the tree terminates with a leaf node indicating a high
probability 90% for “Acadimcs”.else the instances are classified based
on “Fant_arts”into “Arts” with a probability 87%,if equal 0 the
instances are classified into “Academics” with a probability of 74%
,else the instances are classified into “Arts” with a high probability
96%, if “Career_sprt” is 1, the tree further branches based on the
“Fant_arts” into “Sports” with a probability 79%. If “Fant_arts” is 1 ,
there is another split based on the “Time_art” into “Arts” with a
probability 48%. If “Time_art” is greater than or equal to 3, the
instances are classified into “Arts” with a probability of 84% . On the
other hand, if “Time_art” is less than 3, the instances are classified
based on”Act_sprt ” into “Sports” with a probability of 58% .if
“Act_sprt” equal 0 the instances are classified into “Academics” with a
probability of 59%, other hand the instances are classified into
“Sports” with a probability of 78%. If “Fant_arts” not equal 1 , the
instances are classified into “Sports” with a high probability 94%, as
indicated by the leaf node.
Third Confusion matrix
# Predict on the test data
testPred <- predict(Hobby_Data_ctree, newdata = testData, type = 'class')
# Check the accuracy of the model
accuracy <- sum(testPred == testData$`Predicted Hobby`) / nrow(testData) * 100
cat('Accuracy:', accuracy, '\n')
# Create a confusion matrix
conf_matrix <- table(Actual = testData$`Predicted Hobby`, Predicted = testPred)
# Calculate precision for each class
precision_class_1 <- conf_matrix[1, 1] / sum(conf_matrix[1, ])
precision_class_2 <- conf_matrix[2, 2] / sum(conf_matrix[2, ])
precision_class_3 <- conf_matrix[3, 3] / sum(conf_matrix[3, ])
# Calculate sensitivity for each class
sensitivity_class_1 <- conf_matrix[1, 1] / sum(conf_matrix[1, ])
sensitivity_class_2 <- conf_matrix[2, 2] / sum(conf_matrix[2, ])
sensitivity_class_3 <- conf_matrix[3, 3] / sum(conf_matrix[3, ])
# Calculate specificity for each class
specificity_class_1 <- sum(diag(conf_matrix[-1, -1])) / sum(conf_matrix[-1, ])
specificity_class_2 <- sum(conf_matrix[c(1, 3), c(1, 3)]) / sum(conf_matrix[c(1, 3), ])
specificity_class_3 <- sum(conf_matrix[c(1, 2), c(1, 2)]) / sum(conf_matrix[c(1, 2), ])
# Calculate macro-average sensitivity
macro_avg_sensitivity <- (sensitivity_class_1 + sensitivity_class_2 + sensitivity_class_3) / 3
# Calculate macro-average specificity
macro_avg_specificity <- (specificity_class_1 + specificity_class_2 + specificity_class_3) / 3
# Calculate macro-average precision
macro_avg_precision <- (precision_class_1 + precision_class_2 + precision_class_3) / 3
# Print macro-average sensitivity
cat('Average Sensitivity:', macro_avg_sensitivity, '\n')
# Print macro-average specificity
cat('Average Specificity:', macro_avg_specificity, '\n')
# Print macro-average precision
cat('Average Precision:', macro_avg_precision, '\n')
# Print precision for each class
cat('Precision for Class 1:', precision_class_1, ' \n')
cat('Precision for Class 2:', precision_class_2, ' \n')
cat('Precision for Class 3:', precision_class_3, ' \n')
# Print sensitivity for each class
cat('Sensitivity for Class 1:', sensitivity_class_1, '\n')
cat('Sensitivity for Class 2:', sensitivity_class_2, '\n')
cat('Sensitivity for Class 3:', sensitivity_class_3, '\n')
# Print specificity for each class
cat('Specificity for Class 1:', specificity_class_1, '\n')
cat('Specificity for Class 2:', specificity_class_2, '\n')
cat('Specificity for Class 3:', specificity_class_3, '\n')
Comparing Decision Tree Results Using Infromation
gain: After training three trees with different sizes,
employing information gain as the selection measure, our analysis led to
consistent accuracy results among the trees: Tree 1 (0.8932), Tree 2
(0.9057), and Tree 3 (0.8721). The minor discrepancies observed in these
accuracy values could be attributed to the variations in dataset sizes.
Investigating the impact of different training set sizes on model
performance offers valuable insights into the intricate relationship
between data size and accuracy.
In the case of Tree 2, where a larger training set was employed (80%
training, 20% testing), the model had the opportunity to grasp more
robust patterns and relationships within the data. However, it is
crucial to underscore the necessity of striking a balance between the
sizes of the training and testing sets. For Tree 3, with a relatively
smaller testing set (90% training, 10% testing), the accuracy estimate
might be less reliable due to the limited sample size in the testing
set.
In summary, the utilization of information gain as the selection
measure, coupled with different training set sizes, resulted in
comparable accuracy outcomes. Achieving an optimal balance in the sizes
of both training and testing datasets proves essential for ensuring
accurate and generalizable model performance.
+——————+——————-+——————-+——————-+ | information gain | 90 %t raining set
| 80 %t raining set | 70 %t raining set | | | | | | | | 10% testing set:
| 20% testing set: | 30% testing set: |
+:================:+:=================:+:=================:+:=================:+
| Accuracy | 0.872 | 0.905 | 0.893 |
+——————+——————-+——————-+——————-+ | precision | 0.856 |
0.898 | 0.887 | +——————+——————-+——————-+——————-+ |
sensitivity | 0.856 | 0.898 | 0.887 |
+——————+——————-+——————-+——————-+ | specificity | 0.919
| 0.937 | 0.929 | +——————+——————-+——————-+——————-+
Gini index
The Gini index, is a measure used in decision trees, specifically in
the CART (Classification and Regression Trees) algorithm, to quantify
how often a randomly chosen element would be incorrectly labeled if it
was randomly labeled according to the distribution of labels in the
subset. It reflects the probability of a particular variable being
wrongly classified when it is randomly chosen.
1-Gini index(80%,20%)
Install necessary libraries
install.packages("rpart")
install.packages("rpart.plot")
install.packages("caTools")
install.packages("caret")
Load necessary libraries
library(rpart)
library(rpart.plot)
library(caTools)
library(caret)
Set a seed for reproducibility
set.seed(123)
Split the dataset, 80% for training, 20% for testing
split <- sample.split(Hobby_Data$`Predicted Hobby`, SplitRatio = 0.80)
Create the training set (80% of the data)
training_set <- subset(Hobby_Data, split == TRUE)
Create the test set (20% of the data)
test_set <- subset(Hobby_Data, split == FALSE)
Build a decision tree model on the training set
tree <- rpart(`Predicted Hobby` ~ ., data = training_set, method = 'class')
Make predictions on the test set using the tree model
predictions <- predict(tree, test_set, type = "class")
Confusion matrix
conf_matrix <- table(Predicted = predictions, Actual = test_set$`Predicted Hobby`)
Calculate accuracy
accuracy <- sum(diag(conf_matrix)) / sum(conf_matrix)
Initialize vectors to hold the metrics for each class
precision <- numeric(length = nrow(conf_matrix))
recall <- numeric(length = nrow(conf_matrix))
specificity <- numeric(length = nrow(conf_matrix))
Calculate metrics for each class
for (i in 1:nrow(conf_matrix)) {
TP <- conf_matrix[i, i]
FP <- sum(conf_matrix[, i]) - TP
FN <- sum(conf_matrix[i, ]) - TP
TN <- sum(conf_matrix) - TP - FP - FN
precision[i] <- TP / (TP + FP)
recall[i] <- TP / (TP + FN)
specificity[i] <- TN / (TN + FP)
}
Average the metrics if you want a single performance measure
avg_precision <- mean(precision)
avg_recall <- mean(recall)
avg_specificity <- mean(specificity)
Output the evaluation metrics
print(paste("Overall Accuracy:", accuracy))
print(paste("Average Precision:", avg_precision))
print(paste("Average Recall (Sensitivity):", avg_recall))
print(paste("Average Specificity:", avg_specificity))
the metrics for each class:
metrics <- data.frame(
Class = rownames(conf_matrix),
Precision = precision,
Recall = recall,
Specificity = specificity
)
Print metrics
print(metrics)
Plot the decision tree
rpart.plot(tree)
Decision Tree Analysis Using Gini Index(80/20): The
decision tree delineates hobbies into ‘Academics’ (1), ‘Arts’ (2), and
‘Sports’ (3). Without a sports hobby (‘Career_sprt’ = 0), the model
suggests a 62% chance of ‘Academics’. With no arts hobby (‘Fant_arts’ =
0) and ‘Won_arts’ at 0 or 2, there’s a 43% chance of an ‘Academics’
categorization. Conversely, for those with an arts hobby (‘Fant_arts’ =
1) and frequent arts activities (‘Time_art’ ≥ 3), These model show how
likely the model is to predict each hobby based on the attributes’
significance, as learned from the data with a 80% training portion
2-Gini index(90%,10%)
Install necessary libraries
install.packages("rpart")
install.packages("rpart.plot")
install.packages("caTools")
install.packages("caret")
Load necessary libraries
library(rpart)
library(rpart.plot)
library(caTools)
library(caret)
Set a seed for reproducibility
set.seed(123)
Split the dataset, 90% for training, 10% for testing
split <- sample.split(Hobby_Data$`Predicted Hobby`, SplitRatio = 0.90)
Create the training set (90% of the data)
training_set <- subset(Hobby_Data, split == TRUE)
Create the test set (10% of the data)
test_set <- subset(Hobby_Data, split == FALSE)
Build a decision tree model on the training set
tree <- rpart(`Predicted Hobby` ~ ., data = training_set, method = 'class')
Make predictions on the test set using the tree model
predictions <- predict(tree, test_set, type = "class")
Confusion matrix
conf_matrix <- table(Predicted = predictions, Actual = test_set$`Predicted Hobby`)
Calculate accuracy
accuracy <- sum(diag(conf_matrix)) / sum(conf_matrix)
Initialize vectors to hold the metrics for each class
precision <- numeric(length = nrow(conf_matrix))
recall <- numeric(length = nrow(conf_matrix))
specificity <- numeric(length = nrow(conf_matrix))
Calculate metrics for each class
for (i in 1:nrow(conf_matrix)) {
TP <- conf_matrix[i, i]
FP <- sum(conf_matrix[, i]) - TP
FN <- sum(conf_matrix[i, ]) - TP
TN <- sum(conf_matrix) - TP - FP - FN
precision[i] <- TP / (TP + FP)
recall[i] <- TP / (TP + FN)
specificity[i] <- TN / (TN + FP)
}
Average the metrics if you want a single performance measure
avg_precision <- mean(precision)
avg_recall <- mean(recall)
avg_specificity <- mean(specificity)
Output the evaluation metrics
print(paste("Overall Accuracy:", accuracy))
print(paste("Average Precision:", avg_precision))
print(paste("Average Recall (Sensitivity):", avg_recall))
print(paste("Average Specificity:", avg_specificity))
the metrics for each class:
metrics <- data.frame(
Class = rownames(conf_matrix),
Precision = precision,
Recall = recall,
Specificity = specificity
)
Print metrics
print(metrics)
Plot the decision tree
rpart.plot(tree)
Decision Tree Analysis Using Gini Index(90/10):
The decision tree classifies hobbies into ‘Academics’ (1), ‘Arts’
(2), and ‘Sports’ (3). A lack of a sports hobby (‘Career_sprt’ = 0)
leads to a 63% chance of falling into ‘Academics’. If someone is not
engaged in an arts hobby (‘Fant_arts’ = 0) and ‘Won_arts’ is 0 or 2,
there’s a 43% probability of an ‘Academics’ categorization. For
individuals engaged in an arts hobby (‘Fant_arts’ = 1) with a high level
of arts activity (‘Time_art’ ≥ 3), the likelihood of a ‘Sports’
classification is 28%. These model show how likely the model is to
predict each hobby based on the attributes’ significance, as learned
from the data with a 90% training portion.
3-Gini index(70%,30%)
Install necessary libraries
install.packages("rpart")
install.packages("rpart.plot")
install.packages("caTools")
install.packages("caret")
Load necessary libraries
library(rpart)
library(rpart.plot)
library(caTools)
library(caret)
Set a seed for reproducibility
set.seed(123)
Split the dataset, 70% for training, 30% for testing
split <- sample.split(Hobby_Data$`Predicted Hobby`, SplitRatio = 0.70)
Create the training set (70% of the data)
training_set <- subset(Hobby_Data, split == TRUE)
Create the test set (20% of the data)
test_set <- subset(Hobby_Data, split == FALSE)
Build a decision tree model on the training set
tree <- rpart(`Predicted Hobby` ~ ., data = training_set, method = 'class')
Make predictions on the test set using the tree model
predictions <- predict(tree, test_set, type = "class")
Confusion matrix
conf_matrix <- table(Predicted = predictions, Actual = test_set$`Predicted Hobby`)
Calculate accuracy
accuracy <- sum(diag(conf_matrix)) / sum(conf_matrix)
Initialize vectors to hold the metrics for each class
precision <- numeric(length = nrow(conf_matrix))
recall <- numeric(length = nrow(conf_matrix))
specificity <- numeric(length = nrow(conf_matrix))
Calculate metrics for each class
for (i in 1:nrow(conf_matrix)) {
TP <- conf_matrix[i, i]
FP <- sum(conf_matrix[, i]) - TP
FN <- sum(conf_matrix[i, ]) - TP
TN <- sum(conf_matrix) - TP - FP - FN
precision[i] <- TP / (TP + FP)
recall[i] <- TP / (TP + FN)
specificity[i] <- TN / (TN + FP)
}
Average the metrics if you want a single performance measure
avg_precision <- mean(precision)
avg_recall <- mean(recall)
avg_specificity <- mean(specificity)
Output the evaluation metrics
print(paste("Overall Accuracy:", accuracy))
print(paste("Average Precision:", avg_precision))
print(paste("Average Recall (Sensitivity):", avg_recall))
print(paste("Average Specificity:", avg_specificity))
the metrics for each class:
metrics <- data.frame(
Class = rownames(conf_matrix),
Precision = precision,
Recall = recall,
Specificity = specificity
)
Print metrics
print(metrics)
Plot the decision tree
rpart.plot(tree)
Decision Tree Analysis Using Gini Index(70/30):
The decision tree sorts hobbies into ‘Academics’ (1), ‘Arts’ (2), and
‘Sports’ (3). A non-sports hobby (‘Career_sprt’ = 0) results in a 63%
probability of an ‘Academics’ categorization. If ‘Fant_arts’ is 0 and
‘Won_arts’ is 0 or 2, there’s a 43% chance of being classified as
‘Academics’. Conversely, for those involved in an arts hobby
(‘Fant_arts’ = 1) with significant arts activity (‘Time_art’ ≥ 3), the
model indicates a 28% probability of a ‘Sports’ hobby. This decision
tree demonstrates the likelihood of predicting each hobby based on the
importance of the attributes, as determined from the data trained with a
70% portion.
Comparing Decision Tree Results Using Gini
Index:
Across Three Training-Test Sizes: The results of the decision trees
from the 90/10, 80/20, and 70/30 dataset splits, there is a consistent
pattern: ‘Career_sprt’ is always the root node, and the subsequent
splits on ‘Won_arts’ and ‘Fant_arts’ are the same across all trees. This
consistency in tree structure and the probabilities for predicting
‘Academics’ and ‘Sports’ across different splits suggest a stable and
robust model that is reliable regardless of the training set size.
the accuracies of three data splits reveals distinct outcomes: the
(90,10) split leads with the highest accuracy at 0.91875, suggesting
that a larger training portion is more effective in this case. The
(70,30) split follows with an accuracy of 0.91060, showing strong
performance even with a larger test set. However, the commonly used
(80,20) split lags slightly behind, achieving an accuracy of 0.90625.
This comparison highlights the impact of varying training and testing
proportions on model accuracy.
| Accuracy |
0.91875 |
0.906 |
0.911 |
| precision |
0.91850 |
0.905 |
0.909 |
| sensitivity |
0.91927 |
0.909 |
0.912 |
| specificity |
0.9578 |
0.952 |
0.954 |
Gain Ratio The third criterion employed for building
the decision tree is Gain Ratio. Gain Ratio stands out as a significant
metric in decision tree algorithms, especially in scenarios involving
categorical target variables. It normalizes the reduction in entropy by
taking into account the potential information content of the feature.
This normalization process makes Gain Ratio particularly suitable for
datasets with categorical target variables. By factoring in the
intrinsic information of a split, Gain Ratio effectively mitigates bias
towards features with higher levels, ensuring a more balanced evaluation
of different attributes.
1-Gain ratio(90%,10%)
Install necessary libraries
install.packages("C50")
install.packages("printr")
install.packages("caret")
Load necessary libraries
library(C50)
library(printr)
library(caret)
Set a seed for reproducibility
set.seed(1958)
Splitting the data into training and test sets
train_indices <- sample(1:nrow(Hobby_Data), 0.9 * nrow(Hobby_Data))
Hobby.train <- Hobby_Data[train_indices, ]
Hobby.test <- Hobby_Data[-train_indices, ]
Training the decision tree model
model <- C5.0(`Predicted Hobby` ~ ., data = Hobby.train, control = C5.0Control(CF = 0.01))
Making predictions on the test set
predictions <- predict(model, newdata = Hobby.test, type = 'class')
Create a confusion matrix from the predictions and actual values
conf_matrix <- table(Predicted = predictions, Actual = Hobby.test$`Predicted Hobby`)
Calculate and print the accuracy of the model
accuracy <- sum(diag(conf_matrix)) / sum(conf_matrix)
print(paste('Accuracy on test data is:', accuracy))
Initialize vectors to hold the metrics for each class
precision <- numeric(length = nrow(conf_matrix))
recall <- numeric(length = nrow(conf_matrix))
specificity <- numeric(length = nrow(conf_matrix))
Calculate metrics for each class
for (i in 1:nrow(conf_matrix)) {
TP <- conf_matrix[i, i]
FP <- sum(conf_matrix[, i]) - TP
FN <- sum(conf_matrix[i, ]) - TP
TN <- sum(conf_matrix) - TP - FP - FN
precision[i] <- TP / (TP + FP)
recall[i] <- TP / (TP + FN)
specificity[i] <- TN / (TN + FP)
}
Average the metrics if you want a single performance measure
avg_precision <- mean(precision)
avg_recall <- mean(recall)
avg_specificity <- mean(specificity)
Output the evaluation metrics
print(paste("Overall Accuracy:", accuracy))
print(paste("Average Precision:", avg_precision))
print(paste("Average Recall (Sensitivity):", avg_recall))
print(paste("Average Specificity:", avg_specificity))
print the metrics for each class:
metrics <- data.frame(
Class = rownames(conf_matrix),
Precision = precision,
Recall = recall,
Specificity = specificity
)
Print metrics
print(metrics)
Generate and print additional performance metrics using caret
package
confusionMatrix(predictions, Hobby.test$`Predicted Hobby`)
Plot the decision tree
plot(model)
Decision Tree Analysis Using Gain
Ratio(90%/10%):
In First Tree ,we devide dataset into training set and test set with
size(%90,%10) respectively. As you can see in the figure, the root node
is “Career_sprt” , class 1(“Academics”), class 2(“Arts”), and class 3
(“Sports”).
Node “Career_sprt” The first decision is based on whether the value
of the “Career_sprt” attribute is 0. then check if “Won_arts” is either
0 or 2.If” Won_arts” is 0 or 2 , predict”Academic”.then check If
“Fant_arts” is 1 “and Won_arts” is 1, predict “Arts”.If “Fant_arts” is 0
and Won_arts is 0 or 2, then check if Olympiad_Participation is 1.If
Olympiad_Participation is 1 predict “Academics”.(When
Olympiad_Participation is 0) If “Olympiad_Participation” is 0 and
“Fant_arts” is 0, then check if “Grasp_pow” is less than or equal to 4 ,
predict class”Arts”.When Career_sprt is 1, then check if “Fant_arts” is
0 then predict “Sports”. If “Fant_arts” is 1, then check if “Time_art”
is greater than 2.check if “Time_art” is less than or equal to 2, then
check if “Act_sprt” is 1 or 0.If Act_sprt is 1, predict “Sports”. If
Act_sprt is 0, then check if Olympiad_Participation is 0 predict “Arts”.
If “Olympiad_Participation” is 1, predict “Academics”.When Time_art is
greater than 2 ,If Won_arts is 0, predict class “Sports”.
2-Gain ratio(80%,20%)
Install necessary libraries
install.packages("C50")
install.packages("printr")
install.packages("caret")
Load necessary libraries
library(C50)
library(printr)
library(caret)
Set a seed for reproducibility
set.seed(1958)
Splitting the data into training and test sets
train_indices <- sample(1:nrow(Hobby_Data), 0.8 * nrow(Hobby_Data))
Hobby.train <- Hobby_Data[train_indices, ]
Hobby.test <- Hobby_Data[-train_indices, ]
Training the decision tree model
model <- C5.0(`Predicted Hobby` ~ ., data = Hobby.train, control = C5.0Control(CF = 0.01))
Making predictions on the test set
predictions <- predict(model, newdata = Hobby.test, type = 'class')
Create a confusion matrix from the predictions and actual values
conf_matrix <- table(Predicted = predictions, Actual = Hobby.test$`Predicted Hobby`)
Calculate and print the accuracy of the model
accuracy <- sum(diag(conf_matrix)) / sum(conf_matrix)
print(paste('Accuracy on test data is:', accuracy))
Initialize vectors to hold the metrics for each class
precision <- numeric(length = nrow(conf_matrix))
recall <- numeric(length = nrow(conf_matrix))
specificity <- numeric(length = nrow(conf_matrix))
Calculate metrics for each class
for (i in 1:nrow(conf_matrix)) {
TP <- conf_matrix[i, i]
FP <- sum(conf_matrix[, i]) - TP
FN <- sum(conf_matrix[i, ]) - TP
TN <- sum(conf_matrix) - TP - FP - FN
precision[i] <- TP / (TP + FP)
recall[i] <- TP / (TP + FN)
specificity[i] <- TN / (TN + FP)
}
Average the metrics if you want a single performance measure
avg_precision <- mean(precision)
avg_recall <- mean(recall)
avg_specificity <- mean(specificity)
Output the evaluation metrics
print(paste("Overall Accuracy:", accuracy))
print(paste("Average Precision:", avg_precision))
print(paste("Average Recall (Sensitivity):", avg_recall))
print(paste("Average Specificity:", avg_specificity))
print the metrics for each class:
metrics <- data.frame(
Class = rownames(conf_matrix),
Precision = precision,
Recall = recall,
Specificity = specificity
)
Print metrics
print(metrics)
Generate and print additional performance metrics using caret
package
confusionMatrix(predictions, Hobby.test$`Predicted Hobby`)
Plot the decision tree
plot(model)
Decision Tree Analysis Using Gain Ratio(80/20):
The decision tree depicted classifies hobbies into ‘Academics’ (1),
‘Arts’ (2), and ‘Sports’ (3). It starts with ‘Career_sprt’ a value of 0
leads to ‘Won_arts’. If ‘Won_arts’ is 0 or 2, the model suggests
‘Academics’ or ‘Arts’. If ‘Career_sprt’ is 1, ‘Fant_arts’ is considered
next; a value of 0 after ‘Won_arts’ being 1 points towards ‘Arts’, while
a value of 1 leads to ‘Olympiad_Participation’, which, if 1, indicates
‘Academics’. Conversely, a high ‘Grasp_pow’ (>4) predicts
‘Sports’.
3-Gain ratio(70%,30%)
Install necessary libraries
install.packages("C50")
install.packages("printr")
install.packages("caret")
Load necessary libraries
library(C50)
library(printr)
library(caret)
Set a seed for reproducibility
set.seed(1958)
Splitting the data into training and test sets
train_indices <- sample(1:nrow(Hobby_Data), 0.7 * nrow(Hobby_Data))
Hobby.train <- Hobby_Data[train_indices, ]
Hobby.test <- Hobby_Data[-train_indices, ]
Training the decision tree model
model <- C5.0(`Predicted Hobby` ~ ., data = Hobby.train, control = C5.0Control(CF = 0.01))
Making predictions on the test set
predictions <- predict(model, newdata = Hobby.test, type = 'class')
Create a confusion matrix from the predictions and actual values
conf_matrix <- table(Predicted = predictions, Actual = Hobby.test$`Predicted Hobby`)
Calculate and print the accuracy of the model
accuracy <- sum(diag(conf_matrix)) / sum(conf_matrix)
print(paste('Accuracy on test data is:', accuracy))
Initialize vectors to hold the metrics for each class
precision <- numeric(length = nrow(conf_matrix))
recall <- numeric(length = nrow(conf_matrix))
specificity <- numeric(length = nrow(conf_matrix))
Calculate metrics for each class
for (i in 1:nrow(conf_matrix)) {
TP <- conf_matrix[i, i]
FP <- sum(conf_matrix[, i]) - TP
FN <- sum(conf_matrix[i, ]) - TP
TN <- sum(conf_matrix) - TP - FP - FN
precision[i] <- TP / (TP + FP)
recall[i] <- TP / (TP + FN)
specificity[i] <- TN / (TN + FP)
}
Average the metrics if you want a single performance measure
avg_precision <- mean(precision)
avg_recall <- mean(recall)
avg_specificity <- mean(specificity)
Output the evaluation metrics
print(paste("Overall Accuracy:", accuracy))
print(paste("Average Precision:", avg_precision))
print(paste("Average Recall (Sensitivity):", avg_recall))
print(paste("Average Specificity:", avg_specificity))
print the metrics for each class:
metrics <- data.frame(
Class = rownames(conf_matrix),
Precision = precision,
Recall = recall,
Specificity = specificity
)
Print metrics
print(metrics)
Generate and print additional performance metrics using caret
package
confusionMatrix(predictions, Hobby.test$`Predicted Hobby`)
Plot the decision tree
plot(model)
Decision Tree Analysis Using Gain
Ratio(70%/30%):
In Third Tree ,we devide dataset into training set and test set with
size(%70,%30) respectively. As you can see in the figure, the root node
is “Career_sprt” , class 1(“Academics”), class 2(“Arts”), and class 3
(“Sports”).
The first decision is based on whether the value of the “Career_sprt”
attribute is 0.If “Career_sprt” is 0, then check if “Won_arts” is either
0 or 2.predict “Academics”.If “Won_arts” is 1, then check if “Fant_arts”
is 1,predict “Arts”.If ““Fant_arts is 0 and”Won_arts” is 0 or 2, then
check if “Time_art” is less than or equal to 2,predict “Academics”. If
“Time_art” is greater than 2, predict “Arts”.
If “Career_sprt”is 1, then check if “Fant_arts” is 0, predict
“Sports”.If “Fant_arts”is 1, then check if “Time_art” is less than or
equal to 2 check if “Act_sprt” is 0,predict “Academics” .If “Act_sprt”
is 1 predict “Sports”. if “Time_art” is greater than 2, then check if
“Won_arts” is 0 predict “Sports”.If “Won_arts” is 1 or 2 predict
“Arts”.
Comparing Decision Tree Results Using Gain Ratio
Across Three Training-Test Sizes: The accuracy rates -0.8944 for the
90:10 split, 0.8939 for the 70:30 split, and 0.8879 for the 80:20 split
– indicate only slight variations, with the 90:10 split being marginally
better.
| Accuracy |
0.8944 |
0.888 |
0.893970893970894 |
| precision |
0.892 |
0.881 |
0.890 |
| sensitivity |
0.891 |
0.890 |
0.902 |
| specificity |
0.945 |
0.943 |
0.947 |
————————————————-Clustering————————————————————-
str(Hobby_proc)
<<<<<<< Updated upstream
=======
num [1:1601, 1:12] 0.973 0.973 0.973 0.973 0.973 ...
- attr(*, "dimnames")=List of 2
..$ : NULL
..$ : chr [1:12] "Olympiad_Participation" "Scholarship" "Fav_sub" "Projects" ...
- attr(*, "scaled:center")= Named num [1:12] 0.513 0.417 2.112 0.578 3.48 ...
..- attr(*, "names")= chr [1:12] "Olympiad_Participation" "Scholarship" "Fav_sub" "Projects" ...
- attr(*, "scaled:scale")= Named num [1:12] 0.5 0.493 1.05 0.494 1.007 ...
..- attr(*, "names")= chr [1:12] "Olympiad_Participation" "Scholarship" "Fav_sub" "Projects" ...
>>>>>>> Stashed changes
To convert type from factor columns to numeric
Hobby_proc$Fav_sub <-as.numeric(Hobby_proc$Fav_sub)
Hobby_proc$Olympiad_Participation <-as.numeric(Hobby_proc$Olympiad_Participation)
Hobby_proc$Projects <-as.numeric(Hobby_proc$Projects)
Hobby_proc$Scholarship <-as.numeric(Hobby_proc$Scholarship)
Hobby_proc$Career_sprt <-as.numeric(Hobby_proc$Career_sprt)
Hobby_proc$Act_sprt <-as.numeric(Hobby_proc$Act_sprt)
Hobby_proc$Fant_arts <-as.numeric(Hobby_proc$Fant_arts)
Hobby_proc$Won_arts <-as.numeric(Hobby_proc$Won_arts)
Hobby_proc$`Predicted Hobby` <-as.numeric(Hobby_proc$`Predicted Hobby` )
str(Hobby_proc)
<<<<<<< Updated upstream
=======
num [1:1601, 1:12] 0.973 0.973 0.973 0.973 0.973 ...
- attr(*, "scaled:center")= Named num [1:12] 2.57e-17 6.36e-17 -1.29e-16 1.69e-17 1.72e-17 ...
..- attr(*, "names")= chr [1:12] "Olympiad_Participation" "Scholarship" "Fav_sub" "Projects" ...
- attr(*, "scaled:scale")= Named num [1:12] 1 1 1 1 1 1 1 1 1 1 ...
..- attr(*, "names")= chr [1:12] "Olympiad_Participation" "Scholarship" "Fav_sub" "Projects" ...
- attr(*, "dimnames")=List of 2
..$ : NULL
..$ : chr [1:12] "Olympiad_Participation" "Scholarship" "Fav_sub" "Projects" ...
>>>>>>> Stashed changes
Scaled All Columns
Hobby_proc <-scale(Hobby_proc)
Data set without ground truth
Hobby_Data2 <- Hobby_proc[, !(colnames(Hobby_proc) %in% c("Predict Hobby"))]
Our data set exhibits a balanced distribution among class labels,
with “academic,” “art,” and “sport” constituting 43.6%, 25.6%, and 30.8%
of the total, respectively. This balanced distribution is advantageous
for both classification and clustering tasks. In classification, a
balanced data set helps prevent the model from favoring one class over
the others, ensuring that the learning algorithm is exposed to a
representative set of examples from each category. This balance promotes
the development of a model that generalizes well across all classes,
enhancing its predictive performance on new, unseen data. In clustering,
a balanced data set aids in forming clusters that are more evenly
distributed, allowing for a comprehensive understanding of patterns and
relationships across diverse categories. Balanced data sets often lead
to more accurate and fair clustering results, enabling meaningful
insights into the underlying structures within each class.
require packages/Library
install.packages("ggplot2")
install.packages("magrittr")
install.packages("dplyr")
library(factoextra)
library(cluster)
library(dplyr)
———————————————k-meansclustering————————————————
Validation:
Determining the right number of clusters before starting the
clustering process is like making sure you have the correct-sized puzzle
pieces before putting the puzzle together. It’s important because some
clustering algorithms, like K-means, require such a parameter. In
addition to that, it helps make the clustering more accurate and useful.
If you know the right number beforehand, it saves time and helps make
the whole process more efficient and the results more reliable.
compute average silhouette for k clusters using silhouette()
For k-mean
silhouette_score <- function(k) {
km <- kmeans(Hobby_Data2, centers = k, nstart = 25)
ss <- silhouette(km$cluster, dist(Hobby_Data2))
sil <- mean(ss[, 3])
return(sil)
}
# k cluster range from 2 to 10
k <- 2:10
# call function for each k value
avg_sil <- sapply(k, silhouette_score)
# plot the results
plot(k, avg_sil, type = 'b', xlab = 'Number of clusters', ylab = 'Average Silhouette Scores', frame = FALSE)
The results indicate that the highest average silhouette scores
indicate the quality of the clusters and suggest better-defined and more
separated clusters, with each point having a high degree of similarity
to its own cluster and a lower similarity to neighboring clusters. The
highest average silhouette scores were observed for k values of 3, 2,
and 4, so these are the optimal numbers. These values will be employed
in subsequent k-means clustering analyses.
k-means cluster k=3
#set a seed for random number generation to make the results reproducible
set.seed(7)
kmeans.result <- kmeans(Hobby_Data2, 3)
# print the clusterng result
kmeans.result
#visualize clustering
fviz_cluster(kmeans.result, data = Hobby_Data2)
Average for each cluster
avg_sil <- silhouette(kmeans.result$cluster,dist(Hobby_Data2))
fviz_silhouette(avg_sil)
the presence of negative silhouettes for some observations within
each cluster indicates that these points might be more similar to points
in other clusters, suggesting a potential overlap or ambiguity in their
assignment. The fact that some observations have negative silhouettes
highlights that the separation of data points is not entirely
sufficient.
So since 3, which was the optimal number of clusters with the highest
silhouette score average, did not have good clustering results, that
does support our research results that the k-means algorithm is not
applicable to categoricaldata clustering because it relies on the
Euclidean distance metric tomeasure the similarity between data points.
However, even afterEncoding and its application to categorical data pose
significant challenges. Categorical variables often lack a meaningful
numericalrepresentation; for instance, taking the mean of categories
like thefeature “favorite subject,” aka Fav_sub” (even after encoding),
might nothave any practical interpretation. And the distances calculated
in thealgorithm may not reflect the true dissimilarities between
categoricalvalues. The encoding process itself introduces artificial
numericalrelationships that may mislead the algorithm. Moreover, k-means
relieson the minimization of Euclidean distances, which might not
accuratelycapture the dissimilarity structure in categorical data.
Categoricalvariables inherently exhibit discrete and non-ordinal
characteristicsthat are not well-suited for the continuous and linear
assumptions ofk-means. Alternative clustering techniques, specifically
designed forcategorical data, such as partitioning around medoids, are
moreappropriate for capturing the intrinsic patterns and relationships
incategorical datasets.
————————————————–k-mediods clustering with
PAM——————————————–
K-medoids clustering presents a robust alternative for analyzing
categorical data by addressing the limitations posed by k-means
clustering. Unlike k-means, k-medoids does not rely on the mean as a
representative centroid but employs medoids, which are actual data
points within the clusters, and the algorithm defines clusters based on
partitioning around medoids. This feature makes k-medoids particularly
suitable for categorical data, where meaningful centroids may not have a
numerical interpretation. The algorithm defines clusters based on
partitioning around medoids.
Validation:
First, we want to determine three different numbers of clusters by
using a number of methods that will suggest the optimal number of
clusters for k-mediods clustering with PAM.
Silhouette coefficient
fviz_nbclust(Hobby_Data2, pam, method = "silhouette")+
labs(subtitle = "Silhouette method")
Elbow method
fviz_nbclust(Hobby_Data2, pam, method = "wss") +
geom_vline(xintercept= 3, linetype= 3)+
labs(subtitle = "Elbow method")
The silhouette coefficient, which measures the cohesion and
separation of clusters, aligns with the Elbow method, which assesses the
within-cluster sum of squares (wss) as a function of the number of
clusters in the suggested optimal number of clusters. This alignment in
results between two distinct evaluation methods strengthens confidence
in the choice of three clusters, providing a stable foundation for
further analysis and interpretation of the underlying patterns within
the dataset. In addition to that, the ground truth (class labels) also
contains three classes, and that indicates a reassuring alignment
between the structure of the data and the clustering results.
We need to determine two more suggested numbers of clusters by
computing the average silhouette for k clusters using silhouette().
silhouette_score <- function(k) {
km <- pam(Hobby_Data2, k, diss = TRUE)
ss <- silhouette(km$clustering, dist(Hobby_Data2))
sil <- mean(ss[, 3])
return(sil)
}
# k cluster range from 2 to 10
k <- 2:10
# Call function for each k value
avg_sil <- sapply(k, silhouette_score)
# Plot the results
plot(k, avg_sil, type = 'b', xlab = 'Number of clusters', ylab = 'Average Silhouette Scores', frame = FALSE)
It is a common practice to choose the number of clusters
corresponding to the peak in the silhouette score plot, and since we are
looking for two more number of clusters other than three, it would be
reasonable to consider two and four clusters for further analysis.
Especially with the decreasing trend beyond three clusters, it indicates
that adding more clusters does not significantly improve the separation
and cohesion of the clusters.
————————————————–group into k=3
clusters——————————————–
The sub-sampling and clustering approach is a helpful method to
evaluate the robustness of the clustering results under various subsets
and to obtain insights into the structure of the data. Furthermore, we
will take 100 samples from our data set to ensure that the clustering
plot doesn’t get too crowded.
set.seed(7)
# Specify the number of rows you want to sample
num_rows <- 100
# Use sample with the specified seed
idx <- sample(1:dim(Hobby_Data2)[1], num_rows)
Hobby_Data3 <- Hobby_Data2[idx, ]
pam.result <- pam(Hobby_Data3,3)
#Show the silhoutee plot of PAM AND clusters
plot(pam.result)
<<<<<<< Updated upstream
=======


>>>>>>> Stashed changes
# Extract the clusinfo component
clusinfo <- pam.result$clusinfo
# Calculate the total within-cluster sum of squares
tot_withinss <- sum(clusinfo[, "size"] * clusinfo[, "av_diss"]^2)
# Print the result
print(tot_withinss)
<<<<<<< Updated upstream
=======
str(Hobby_Data2)
num [1:1601, 1:11] 0.973 0.973 0.973 0.973 0.973 ...
- attr(*, "dimnames")=List of 2
..$ : NULL
..$ : chr [1:11] "Olympiad_Participation" "Scholarship" "Fav_sub" "Projects" ...
Data set without ground truth
Hobby_Data2 <- Hobby_proc[, !(colnames(Hobby_proc) %in% c("Predicted Hobby"))]
summary(Hobby_Data2)
Olympiad_Participation Scholarship Fav_sub Projects Grasp_pow Time_sprt Career_sprt Act_sprt
Min. :-1.0269 Min. :-0.8459 Min. :-1.0586 Min. :-1.1709 Min. :-2.4637 Min. :-1.50901 Min. :-0.7698 Min. :-1.1199
1st Qu.:-1.0269 1st Qu.:-0.8459 1st Qu.:-1.0586 1st Qu.:-1.1709 1st Qu.:-0.4771 1st Qu.:-0.76702 1st Qu.:-0.7698 1st Qu.:-1.1199
Median : 0.9732 Median :-0.8459 Median :-0.1065 Median : 0.8535 Median :-0.4771 Median :-0.02503 Median :-0.7698 Median : 0.8924
Mean : 0.0000 Mean : 0.0000 Mean : 0.0000 Mean : 0.0000 Mean : 0.0000 Mean : 0.00000 Mean : 0.0000 Mean : 0.0000
3rd Qu.: 0.9732 3rd Qu.: 1.1815 3rd Qu.: 0.8457 3rd Qu.: 0.8535 3rd Qu.: 0.5162 3rd Qu.: 0.71696 3rd Qu.: 1.2981 3rd Qu.: 0.8924
Max. : 0.9732 Max. : 1.1815 Max. : 1.7978 Max. : 0.8535 Max. : 2.5028 Max. : 2.20095 Max. : 1.2981 Max. : 0.8924
Fant_arts Won_arts Time_art
Min. :-0.8733 Min. :-0.8648 Min. :-0.9748
1st Qu.:-0.8733 1st Qu.:-0.8648 1st Qu.:-0.9748
Median :-0.8733 Median :-0.8648 Median :-0.1850
Mean : 0.0000 Mean : 0.0000 Mean : 0.0000
3rd Qu.: 1.1443 3rd Qu.: 0.4042 3rd Qu.: 0.6048
Max. : 1.1443 Max. : 1.6733 Max. : 2.9741
str(Hobby_Data2)
List of 17612
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
[list output truncated]
# Assuming 'pam.result' is the result from PAM clustering
Hobby_Data2$cluster <- as.factor(pam.result$clustering)
Warning: Coercing LHS to a list
# Make sure 'cluster' is a factor
Hobby_Data2$cluster <- as.factor(Hobby_Data2$cluster)
# Check the structure of Hobby_Data2
str(Hobby_Data2)
List of 17612
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
$ : num 0.973
[list output truncated]
# Create parallel coordinate plot
p <- ggparcoord(
data = Hobby_Data2,
columns = c(1:11), # Assuming there are 11 columns in your data
groupColumn = "cluster",
scale = "std"
) + labs(x = "milk constituent", y = "value (in standard-deviation units)", title = "Clustering")
# Convert the ggplot object to plotly
p_plotly <- ggplotly(p)
# Print the plot
print(p_plotly)
NULL
>>>>>>> Stashed changes
The output of the code, displaying a silhouette plot of the PAM
clusters, indicates that the clusters are relatively close to each
other, with a slight overlap between two clusters. The silhouette plot
visually represents how well-defined and separated the clusters are.
While the slight overlap suggests that the natural grouping within the
dataset may not be entirely distinct, it does not negatively affect the
overall quality of the clusters. In fact, the observed overlap might
indicate shared characteristics between adjacent clusters, effectively
capturing meaningful patterns and groupings that reflect the intricacies
of real-world phenomena not confined to strict boundaries.
bCubed
cluster_assignments <- c(pam.result$cluster)
set.seed(7)
# Specify the number of rows you want to sample
num_rows <- 100
# Use sample with the specified seed
idx <- sample(1:dim(Hobby_proc)[1], num_rows)
# Select the sampled rows from Hobby_proc
Hobby_Data4 <- Hobby_proc[idx, ]
ground_truth_labels <- c(Hobby_Data4)
# Create a data frame with cluster assignments and ground truth labels
dataset <- data.frame(cluster = cluster_assignments, label = ground_truth_labels)
# Calculate BCubed precision and recall
calculate_bcubed_metrics <- function(dataset) {
n <- nrow(dataset)
precision_sum <- 0
recall_sum <- 0
for (i in 1:n) {
cluster <- dataset$cluster[i]
label <- dataset$label[i]
# Count the number of items from the same category in its cluster
same_category <- sum(dataset$label[dataset$cluster == cluster] == label)
# Count the number of items in its cluster
same_cluster <- sum(dataset$cluster == cluster)
# Count the number of items in its category
total_same_category <- sum(dataset$label == label)
# Calculate precision and recall
precision_sum <- precision_sum + same_category / same_cluster
recall_sum <- recall_sum + same_category / total_same_category
}
# End loop
# Calculate average precision and recall
precision <- precision_sum / n
recall <- recall_sum / n
return(list(precision = precision, recall = recall))}
# Calculate BCubed precision and recall
metrics <- calculate_bcubed_metrics(dataset)
precision <- metrics$precision
recall <- metrics$recall
# Print the results
cat("BCubed Precision= ", precision, "AND BCubed Recall= ", recall, "\n")
While precision highlights room for better accuracy in identifying
similar items, the higher recall indicates the algorithm’s capability to
catch a good amount of actual similarities within clusters.
————————————————–group into k=4
clusters————————————————
set.seed(7)
# Specify the number of rows you want to sample
num_rows <- 100
# Use sample with the specified seed
idx <- sample(1:dim(Hobby_Data2)[1], num_rows)
Hobby_Data3 <- Hobby_Data2[idx, ]
pam.result <- pam(Hobby_Data3,4)
#Show the silhoutee plot of PAM AND clusters
plot(pam.result)
# Extract the clusinfo component
clusinfo <- pam.result$clusinfo
# Calculate the total within-cluster sum of squares
tot_withinss <- sum(clusinfo[, "size"] * clusinfo[, "av_diss"]^2)
# Print the result
print(tot_withinss)
The output suggests that the dataset may exhibit a degree of overlap
or similarity among observations. The overlapping clusters may indicate
challenges in achieving a clear separation among these groups. The
placement of one cluster on top of two others implies that the medoid of
this cluster might be near points belonging to those two neighboring
clusters. This could be due to the nature of the data.
cluster_assignments <- pam.result$clustering
# Add cluster assignments to the original data
Hobby_Data_with_clusters <- cbind(Hobby_Data3, Cluster = factor(cluster_assignments))
# Example: Pair plot for the first four features
pairs(Hobby_Data_with_clusters[, 1:4], col = cluster_assignments)
BCubed
cluster_assignments <- c(pam.result$cluster)
set.seed(7)
# Specify the number of rows you want to sample
num_rows <- 100
# Use sample with the specified seed
idx <- sample(1:dim(Hobby_proc)[1], num_rows)
# Select the sampled rows from Hobby_proc
Hobby_Data4 <- Hobby_proc[idx, ]
# Create a data frame with cluster assignments and ground truth labels
dataset <- data.frame(cluster = cluster_assignments, label = ground_truth_labels)
# Calculate BCubed precision and recall
calculate_bcubed_metrics <- function(dataset) {
n <- nrow(dataset)
precision_sum <- 0
recall_sum <- 0
for (i in 1:n) {
cluster <- dataset$cluster[i]
label <- dataset$label[i]
# Count the number of items from the same category in its cluster
same_category <- sum(dataset$label[dataset$cluster == cluster] == label)
# Count the number of items in its cluster
same_cluster <- sum(dataset$cluster == cluster)
# Count the number of items in its category
total_same_category <- sum(dataset$label == label)
# Calculate precision and recall
precision_sum <- precision_sum + same_category / same_cluster
recall_sum <- recall_sum + same_category / total_same_category
}
# End loop
# Calculate average precision and recall
precision <- precision_sum / n
recall <- recall_sum / n
return(list(precision = precision, recall = recall))
}
# Calculate BCubed precision and recall
metrics <- calculate_bcubed_metrics(dataset)
precision <- metrics$precision
recall <- metrics$recall
# Print the results
cat("BCubed Precision= ", precision, "AND BCubed Recall= ", recall, "\n")
The results indicate challenges in clustering performance. The low
precision suggests a significant rate of misclassification, while the
relatively low recall indicates that some instances within the same
group are missed or incorrectly assigned to other clusters. These
results highlight limitations in accurately capturing the data’s
underlying structure.
———————————————-group into k=2
clusters——————————————–
set.seed(7)
# Specify the number of rows you want to sample
num_rows <- 100
# Use sample with the specified seed
idx <- sample(1:dim(Hobby_Data2)[1], num_rows)
Hobby_Data3 <- Hobby_Data2[idx, ]
pam.result <- pam(Hobby_Data3,2)
#Show the silhoutee plot of PAM AND clusters
plot(pam.result)
# Extract the clusinfo component
clusinfo <- pam.result$clusinfo
# Calculate the total within-cluster sum of squares
tot_withinss <- sum(clusinfo[, "size"] * clusinfo[, "av_diss"]^2)
# Print the result
print(tot_withinss)
An overlap between clusters implies that there is ambiguity in the
assignment of data points to clusters, and the clusters may not be
sufficiently distinct. In such cases, it might be needed to reconsider
the number of clusters since the goal is to find a balance, but too few
clusters result in oversimplification, as indicated by the observed
overlap, and it is evident that forming only two clusters may not be
sufficient to represent the inherent structure of the dataset.
#BCubed
cluster_assignments <- c(pam.result$cluster)
set.seed(7)
# Specify the number of rows you want to sample
num_rows <- 100
# Use sample with the specified seed
idx <- sample(1:dim(Hobby_proc)[1], num_rows)
# Select the sampled rows from Hobby_proc
Hobby_Data4 <- Hobby_proc[idx, ]
# Create a data frame with cluster assignments and ground truth labels
dataset <- data.frame(cluster = cluster_assignments, label = ground_truth_labels)
# Calculate BCubed precision and recall
calculate_bcubed_metrics <- function(dataset) {
n <- nrow(dataset)
precision_sum <- 0
recall_sum <- 0
for (i in 1:n) {
cluster <- dataset$cluster[i]
label <- dataset$label[i]
# Count the number of items from the same category in its cluster
same_category <- sum(dataset$label[dataset$cluster == cluster] == label)
# Count the number of items in its cluster
same_cluster <- sum(dataset$cluster == cluster)
# Count the number of items in its category
total_same_category <- sum(dataset$label == label)
# Calculate precision and recall
precision_sum <- precision_sum + same_category / same_cluster
recall_sum <- recall_sum + same_category / total_same_category
}
# End loop
# Calculate average precision and recall
precision <- precision_sum / n
recall <- recall_sum / n
return(list(precision = precision, recall = recall))
}
# Calculate BCubed precision and recall
metrics <- calculate_bcubed_metrics(dataset)
precision <- metrics$precision
recall <- metrics$recall
# Print the results
cat("BCubed Precision= ", precision, "AND BCubed Recall= ", recall, "\n")
The relatively high recall could be influenced by the specific choice
of two clusters since it is sensitive to the number of clusters. In the
context of a two-cluster solution, the recall score reflects how well
the algorithm groups data points from the same class into one of the two
identified clusters. Recall suggests that a significant portion of data
points from the same ground truth class are indeed grouped together in
one of the two clusters. However, it’s important to note that the low
precision score (0.0432) indicates a lack of homogeneity within the
identified clusters, implying that the clusters contain a mix of data
points from different ground truth classes.
AS A Summary For clustring Silhouette analysis
measures how similar an object is to its own cluster (cohesion) compared
to other clusters (separation). The silhouette width ranges from -1 to
1, where a high value indicates that the object is well matched to its
own cluster and poorly matched to neighboring clusters. In our project
we did clusters for k=3, k=4, and k=2 since its have higher average
silhouette then other number.
For k=3, the average silhouette width is 0.22. For k=4, the average
silhouette width is 0.19. For k=2, the average silhouette width is
0.21.
A higher average silhouette width generally indicates better-defined
clusters. So, in this case, k=3 has the highest average silhouette
width.
BCubed is a clustering evaluation metric that considers both
precision and recall. Precision measures the accuracy of the positive
predictions, while recall measures the coverage of the actual positive
instances.
For k=3, BBCubed Precision = 0.0488 BCubed Recall = 0.4932 For
k=4,BCubed Precision = 0.0505 BCubed Recall = 0.4101 For k=2,
BCubedPrecision = 0.0432 BCubed Recall = 0.6252
These metrics measure how well the clustering aligns with the ground
truth. Higher precision indicates fewer false positives, and higher
recall indicates fewer false negatives. Here, k=2 has the highest recall
(0.6252), but k=3 has a reasonable balance between precision and
recall.
In conclusion, k=3 seems to be a reasonable choice. It has a good
silhouette width, and its BCubed Precision and Recall values strike a
balance.
library(plotly)
Registered S3 method overwritten by 'htmlwidgets':
method from
print.htmlwidget tools:rstudio
Attaching package: ‘plotly’
The following object is masked from ‘package:ggplot2’:
last_plot
The following object is masked from ‘package:stats’:
filter
The following object is masked from ‘package:graphics’:
layout
Findings
|
IG |
IG ratio |
Gini Index |
IG |
IG ratio |
Gini Index |
IG |
IG ratio |
Gini Index |
| Accuracy |
0.872 |
0.8944 |
0.91875 |
0.905 |
0.888 |
0.906 |
0.893 |
0.8939 |
0.911 |
| precision |
0.856 |
0.892 |
0.91850 |
0.898 |
0.881 |
0.905 |
0.887 |
0.890 |
0.909 |
| sensitiviy |
0.856 |
0.891 |
0.91927 |
0.898 |
0.890 |
0.909 |
0.887 |
0.902 |
0.912 |
| specificiy |
0.919 |
0.945 |
0.9578 |
0.937 |
0.943 |
0.952 |
0.929 |
0.947 |
0.954 |
References
What is the best for our dataset?
since We used the Information Gain, Gini Index, and Gain Ratio as
three important metrics in our categorical data classification approach
to determine how important certain variables were in predicting hobby
categories. The dataset was split into training and testing sets so that
different subsets of the data could be used to train classification
algorithms. Three metrics were used to evaluate the performance of the
model: overall accuracy, recall, and precision.We examined accuracy at
several measures of tree selection, Provide us with more accurate
results, specifically at the pruning parameters of 0.90, 0.91, and 0.89,
in order.The methodical evaluation yielded insightful information on how
well each selection measure performed in producing precise predictions
within the dataset. high accuracy strongly suggest that, for our
dataset, classification is a better methodology than clustering, since
the classification give us a good result then clustring.
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyAqKktpZHMgSG9iYnkgUHJlZGljdGlvbiBEYXRhc2V0KioNCg0KDQoqKlByb2JsZW0qKg0KDQpDaGlsZHJlbiBvZnRlbiBmaW5kIHRoZW1zZWx2ZXMgYXQgYSBjcm9zc3JvYWRzIHdoZW4gaXQgY29tZXMgdG8gZGlzY292ZXJpbmcgdGhlaXIgcGFzc2lvbnMgb3IgaG9iYmllcywgYmUgaXQgaW4gYWNhZGVtaWNzLCBhcnRzLCBvciBzcG9ydHMuIFJlY29nbml6aW5nIHRoZSBpbXBvcnRhbmNlIG9mIGd1aWRpbmcgY2hpbGRyZW4gdG93YXJkcyBhY3Rpdml0aWVzIHRoZXkgYXJlIHBhc3Npb25hdGUgYWJvdXQ7IHdlIGhhdmUgY3VyYXRlZCBhIGRhdGFzZXQgb2J0YWluZWQgdGhyb3VnaCBzdXJ2ZXlzIGNvbmR1Y3RlZCB3aXRoIHBhcmVudHMuIFRoaXMgZGF0YXNldCBjb21waWxlcyB2YWx1YWJsZSBpbmZvcm1hdGlvbiBhYm91dCBjaGlsZHJlbidzIHByZWZlcmVuY2VzLCBlbmFibGluZyB0aGUgY3JlYXRpb24gb2YgYSBjbGFzc2lmaWNhdGlvbiBtb2RlbCBhaW1lZCBhdCBwcmVkaWN0aW5nIGtpZHMnIGhvYmJpZXMuIA0KDQpJbiB0aGlzIGV4cGxvcmF0b3J5IGpvdXJuZXksIHdlIGRlbHZlIGludG8gdGhlIGRhdGFzZXQgdG8gdW5jb3ZlciBwYXR0ZXJucyBhbmQgaW5zaWdodHMgdGhhdCBjYW4gYXNzaXN0IHBhcmVudHMgaW4gdW5kZXJzdGFuZGluZyB0aGVpciBjaGlsZCdzIGluY2xpbmF0aW9ucyBiZXR0ZXIuIFRocm91Z2ggdGhlIGFwcGxpY2F0aW9uIG9mIGNsdXN0ZXJpbmcgdGVjaG5pcXVlcywgd2UgYWltIHRvIGNhdGVnb3JpemUgY2hpbGRyZW4gYmFzZWQgb24gbnVtYmVyIG9mIGNvbHVtbnMgKEZhdl9zdWIgLCBTY2hvbGFyc2hpcCwgZXRjLi4pLiBUaGUgdWx0aW1hdGUgZ29hbCBpcyB0byBwcm92aWRlIHBhcmVudHMgd2l0aCBtZWFuaW5nZnVsIHJlY29tbWVuZGF0aW9ucywgZm9zdGVyaW5nIGFuIGVudmlyb25tZW50IHdoZXJlIGNoaWxkcmVuIGNhbiB0aHJpdmUgaW4gYWN0aXZpdGllcyB0aGF0IGdlbnVpbmVseSByZXNvbmF0ZSB3aXRoIHRoZWlyIGludGVyZXN0cy4gDQoNCkJ5IGFuYWx5emluZyB0aGlzIGRhdGFzZXQsIHdlIGhvcGUgdG8gb2ZmZXIgaW5zaWdodGZ1bCBpbmZvcm1hdGlvbiB0aGF0IHdpbGwgYWlkIHBhcmVudHMgaW4gZ3VpZGluZyB0aGVpciBraWRzIHRvd2FyZCB0aGUgcmlnaHQgaG9iYmllcy4gDQoNCg0KKipEYXRhIE1pbmluZyBUYXNrKioNCg0KVGhlIGRhdGEgbWluaW5nIHRhc2sgYXQgaGFuZCByZXZvbHZlcyBhcm91bmQgcHJlZGljdGluZyBraWRzJyBob2JiaWVzIGJhc2VkIG9uIGEgZGF0YXNldCBuYW1lZCAiSG9iYnlfRGF0YSwiIG9idGFpbmVkIHRocm91Z2ggc3BlY2lmaWMgcXVlc3Rpb25zIHBvc2VkIHRvIHRoZWlyIHBhcmVudHMgcmVnYXJkaW5nIHByZWZlcmVuY2VzLCBjYXBhYmlsaXRpZXMsIGFuZCBhY2hpZXZlbWVudHMuIFRoaXMgZGF0YXNldCBzZXJ2ZXMgYXMgdGhlIGZvdW5kYXRpb24gZm9yIHR3byBwcmltYXJ5IGRhdGEgbWluaW5nIHRhc2tzOiBjbGFzc2lmaWNhdGlvbiBhbmQgY2x1c3RlcmluZy4gSW4gdGhlIGNsYXNzaWZpY2F0aW9uIHByb2Nlc3MsIHRoZSBnb2FsIGlzIHRvIHRyYWluIGEgbWFjaGluZSBsZWFybmluZyBtb2RlbCB0byBiZSBjYXBhYmxlIG9mIGFjY3VyYXRlbHkgcHJlZGljdGluZyBhIGNoaWxkJ3MgaG9iYnkgYXMgZWl0aGVyICJhY2FkZW1pYywiICJhcnQsIiBvciAic3BvcnRzLiIgVGhpcyByZXF1aXJlcyB1c2luZyB0aGUgZGF0YSBjb2xsZWN0ZWQgZnJvbSBwYXJlbnRzIHRvIGVzdGFibGlzaCBwYXR0ZXJucyBhbmQgcmVsYXRpb25zaGlwcyB0aGF0IGd1aWRlIHRoZSBtb2RlbCBpbiBtYWtpbmcgYWNjdXJhdGUgcHJlZGljdGlvbnMuIENvbmN1cnJlbnRseSwgdGhlIGNsdXN0ZXJpbmcgcHJvY2VzcyBpbnZvbHZlcyBwYXJ0aXRpb25pbmcgdGhlIGRhdGFzZXQgaW50byBtZWFuaW5nZnVsIGNsdXN0ZXJzLCBncm91cGluZyB0b2dldGhlciBjaGlsZHJlbiB3aXRoIHNpbWlsYXIgY2hhcmFjdGVyaXN0aWNzIG9yIHByZWZlcmVuY2VzLiBUaHJvdWdoIHRoZXNlIGR1YWwgYXBwcm9hY2hlcywgdGhlIGRhdGEgbWluaW5nIHRhc2sgYWltcyB0byBkZXZlbG9wIGEgcm9idXN0IHByZWRpY3RpdmUgbW9kZWwgY2FwYWJsZSBvZiBhY2N1cmF0ZWx5IGNhdGVnb3JpemluZyBjaGlsZHJlbidzIGhvYmJpZXMgaW4gb3JkZXIgdG8gdW5jb3ZlciB0aGUgaW5oZXJlbnQgc3RydWN0dXJlcywgcGF0dGVybnMsIGFuZCBhc3NvY2lhdGlvbnMgd2l0aGluIHRoZSBkYXRhc2V0LCBjb250cmlidXRpbmcgdG8gYSBkZWVwZXIgdW5kZXJzdGFuZGluZyBvZiB0aGUgZGl2ZXJzZSBpbnRlcmVzdHMgYW5kIGVuZ2FnZW1lbnQgbGV2ZWxzIG9mIHRoZSB5b3VuZyBwb3B1bGF0aW9uIGluIGFjYWRlbWljLCBhcnRpc3RpYywgYW5kIHNwb3J0cy1yZWxhdGVkIGFjdGl2aXRpZXMuDQogDQogDQoNCg0KKipEYXRhKioNCg0KVGhlIHNvdXJjZTogaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9kYXRhc2V0cy9hYnRhYm0vaG9iYnktcHJlZGljdGlvbi1iYXNpYyANCk51bWJlciBvZiBvYmplY3RzOiAxNjAxIA0KDQpOdW1iZXIgb2YgYXR0cmlidXRlczogMTQgDQoNCg0KKipEZXNjcmlwdGlvbjoqKg0KRm9sbG93aW5nIHRoZSBzZWxlY3Rpb24gb2Ygb3VyIGRhdGEgc2V0ICgiSG9iYnlfRGF0YSIpIHdoaWNoIHByZWRpY3RzIGtpZHMnIGhvYmJpZXMsIHRoYXQgd2FzIGNvbGxlY3RlZCBieSBhc2tpbmcgdGhlaXIgcGFyZW50cyBzcGVjaWZpYyBxdWVzdGlvbnMgYWJvdXQgdGhlaXIga2lkJ3MgcHJlZmVyZW5jZXMsIGNhcGFiaWxpdGllcywgYW5kIGFjaGlldmVtZW50cy4gVG8gaGVscCB1cyB0cmFpbiB0aGUgbWFjaGluZSB0byBwcmVkaWN0IHRoZSBraWQncyBob2JieS4gV2Ugd2lsbCBiZWdpbiB0byBwcmVwcm9jZXNzIGFuZCBhbmFseXplIHRoZSBkYXRhLg0KDQoNCg0KKipBdHRyaWJ1dGUqKiAqKkRlc2NyaXB0aW9uOioqDQoNCnwgQXR0cmlidXRlIG5hbWUgICAgICAgICB8ICoqRGVzY3JpcHRpb24qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAqKkRhdGEgdHlwZSoqIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLXwNCnwgT2x5bXBpYWRfUGFydGljaXBhdGlvbiB8IEhhcyB5b3VyIGNoaWxkIHBhcnRpY2lwYXRlZCBpbiBhbnkgU2NpZW5jZS9NYXRocyAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgU2Nob2xhcnNoaXAgICAgICAgICAgICB8IEhhcyBoZS9zaGUgcmVjZWl2ZWQgYW55IHNjaG9sYXJzaGlwPyAgICAgICAgICAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgU2Nob29sICAgICAgICAgICAgICAgICB8IExvdmUncyBnb2luZyB0byBzY2hvb2w/ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgRmF2X3N1YiAgICAgICAgICAgICAgICB8IFdoYXQgaXMgaGlzL2hlciBmYXZvcml0ZSBzdWJqZWN0PyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBDYXRlZ29yaWNhbCAgIHwNCnwgUHJvamVjdHMgICAgICAgICAgICAgICB8IEhhcyBkb25lIGFueSBwcm9qZWN0cyB1bmRlciBhY2FkZW1pY3MgYmVmb3JlPyAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgR3Jhc3BfcG93ICAgICAgICAgICAgICB8IEhpcy9IZXIgR3Jhc3BpbmcgcG93ZXIgKDEtNikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBPcmRpbmFsICAgICAgIHwNCnwgVGltZV9zcHJ0ICAgICAgICAgICAgICB8IEhvdyBtdWNoIHRpbWUgZG9lcyBoZS9zaGUgc3BlbmQgcGxheWluZyBvdXRkb29yL2luZG9vciBnYW1lcz8gfCBPcmRpbmFsICAgICAgIHwNCnwgTWVkYWxzICAgICAgICAgICAgICAgICB8IE1lZGFscyB3b24gaW4gU3BvcnRzPyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgQ2FyZWVyX3NwcnQgICAgICAgICAgICB8IFdhbnQncyB0byBwdXJzdWUgaGlzL2hlciBjYXJlZXIgaW4gc3BvcnRzPyAgICAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgQWN0X3NwcnQgICAgICAgICAgICAgICB8IFJlZ3VsYXIgaW4gaGlzL2hlciBzcG9ydHMgYWN0aXZpdGllcz8gICAgICAgICAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgRmFudF9hcnRzICAgICAgICAgICAgICB8IExvdmUgY3JlYXRpbmcgZmFudGFzeSBwYWludGluZ3M/ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgV29uX2FydHMgICAgICAgICAgICAgICB8IFdvbiBhcnQgY29tcGV0aXRpb25zPyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBPcmRpbmFsICAgICAgIHwNCnwgVGltZV9hcnQgICAgICAgICAgICAgICB8IFRpbWUgdXRpbGl6ZWQgaW4gQXJ0cz8gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBPcmRpbmFsICAgICAgIHwNCnwgUHJlZGljdGVkIEhvYmJ5ICAgICAgICB8IHByZWRpY3Rpb25zIGZvciB0aGUgaG9iYnkgdGhhdCB0aGUga2lkIHdvdWxkbCBpa2UgICAgICAgICAgICAgfCBDYXRlZ29yaWNhbCAgIHwNCj09PT09PT0NCj4+Pj4+Pj4gU3Rhc2hlZCBjaGFuZ2VzDQoNCioqR2VuZXJhbCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgZGF0YSBzZXQ6KioNCg0KYGBge3J9DQpzdHIoSG9iYnlfRGF0YSkNCmBgYA0KDQotICAgKipzYW1wbGVzIG9mIHJhdyBkYXRhc2V0OioqDQoNCmBgYHtyfQ0Kc2FtcGxlKEhvYmJ5X0RhdGEpDQpgYGANCg0KLSAgICoqdmFyaWFibGVzIGRpc3RyaWJ1dGlvbjoqKg0KDQogICAgSW4gb3VyIGRhdGFzZXQsIG51bWVyaWMgdmFyaWFibGVzIGFyZSBub3QgYXZhaWxhYmxlOyBpbnN0ZWFkLCB3ZSBoYXZlIHRocmVlIG9yZGluYWwgdmFyaWFibGVzLiBEdWUgdG8gdGhlIG5hdHVyZSBvZiBvdXIgZGF0YSB0eXBlcywgY2VydGFpbiB0eXBlcyBvZiBncmFwaHMsIHN1Y2ggYXMgc2NhdHRlciBwbG90cyBhbmQgYm94IHBsb3RzLCB3ZXJlIG5vdCBzdWl0YWJsZSBmb3Igb3VyIGFuYWx5c2lzLg0KDQp2YXJpYWJsZXMgZGlzdHJpYnV0aW9uIG9mIFRpbWVfc3BydDoNCg0KYGBge3J9DQppbnN0YWxsLnBhY2thZ2VzKCJtYWdyaXR0ciIpICMgaW5zdGFsbCBvbmx5IG9uZSB0aW1lIHRoZW4gcHV0IHRoaXMgY29tbWFuZCBhcyBjb21tZW50IGFmdGVyIGluc3RhbGxhdGlvbg0KbGlicmFyeShtYWdyaXR0cikgIyMgZm9yIHBpcGUgb3BlcmF0aW9ucw0KSG9iYnlfRGF0YSRUaW1lX2FydCAlPiUgZGVuc2l0eSgpICU+JSBwbG90KG1haW49J3ZhcmlhYmxlcyBkaXN0cmlidXRpb24gb2YgVGltZV9hcnQnKQ0KDQpgYGANCg0KSW4gdGhlICJUaW1lX2FydCIgdmFyaWFibGUsIHBhcmVudHMgd2VyZSByZXF1ZXN0ZWQgdG8gYXNzZXNzIHRoZSB0aW1lIHRoZWlyIGNoaWxkIGRlZGljYXRlZCB0byBhcnRpc3RpYyBwdXJzdWl0cyBsaWtlIHBhaW50aW5nIG9yIHBhcGVyIGNyYWZ0aW5nLCB1c2luZyBhIHNjYWxlIHJhbmdpbmcgZnJvbSAxIHRvIDYsIHdoZXJlIDYgcmVwcmVzZW50cyB0aGUgaGlnaGVzdCBsZXZlbCBvZiBpbnZvbHZlbWVudC4gSXQncyB3b3J0aCBub3RpbmcgdGhhdCB0aGUgY29uY2VudHJhdGlvbiBvZiBsb3dlciByYXRpbmdzIGF0IHRoZSBsb3dlciBlbmQgb2YgdGhlIHNjYWxlICgxKSBpcyBxdWl0ZSBwcm9ub3VuY2VkLCBhbmQgdGhpcyB0ZW5kZW5jeSBtYXkgYmUgYXR0cmlidXRlZCB0byB0aGUgaW5oZXJlbnQgaW5jbGluYXRpb24gb2YgY2hpbGRyZW4gdG93YXJkcyBwaHlzaWNhbCBhY3Rpdml0aWVzLg0KDQp2YXJpYWJsZXMgZGlzdHJpYnV0aW9uIG9mIFRpbWVfYXJ0Og0KDQpgYGB7cn0NCkhvYmJ5X0RhdGEkVGltZV9zcHJ0ICU+JSBkZW5zaXR5KCkgJT4lIHBsb3QobWFpbj0ndmFyaWFibGVzIGRpc3RyaWJ1dGlvbiBvZiBUaW1lX3NwcnQnKQ0KYGBgDQoNClBhcmVudHMgd2VyZSByZXF1ZXN0ZWQgdG8gYXNzZXNzIHRoZWlyIGNoaWxkcmVuJ3MgaW52b2x2ZW1lbnQgaW4gc3BvcnRzIG9uIGEgc2NhbGUgZnJvbSAxIHRvIDYgd2l0aGluIHRoZSAiVGltZV9zcHJ0IiB2YXJpYWJsZS4gTm90YWJseSwgdGhlIG1vc3QgcHJldmFsZW50IHJhbmtpbmcgd2FzIDMsIHN1Z2dlc3RpbmcgYSBtb2RlcmF0ZSBsZXZlbCBvZiBzcG9ydHMgcGFydGljaXBhdGlvbi4gSXQncyBpbnRlcmVzdGluZyB0byBvYnNlcnZlIHRoYXQgdGhlIGRpc3RyaWJ1dGlvbiBleGhpYml0cyBhIHNoYXBlIGFraW4gdG8gYSBiZWxsIGN1cnZlLCBpbmRpY2F0aW5nIHRoYXQgYSBzdWJzdGFudGlhbCBwcm9wb3J0aW9uIG9mIGNoaWxkcmVuIGhhdmUgYSBnZW51aW5lIGxvdmUgZm9yIHNwb3J0cy4NCg0KdmFyaWFibGVzIGRpc3RyaWJ1dGlvbiBvZiBHcmFzcF9wb3c6DQoNCmBgYHtyfQ0KSG9iYnlfRGF0YSRHcmFzcF9wb3cgJT4lIGRlbnNpdHkoKSAlPiUgcGxvdChtYWluPSd2YXJpYWJsZXMgZGlzdHJpYnV0aW9uIG9mIEdyYXNwX3BvdycpDQpgYGANCg0KVGhlIGRlbnNpdHkgZ3JhcGggZGVwaWN0aW5nIHBhcmVudHMnIHJhdGluZ3Mgb2YgdGhlaXIgY2hpbGRyZW4ncyBncmFzcCBwb3dlciwgd2hpY2ggcmFuZ2VzIGZyb20gMSB0byA2LCBpbGx1c3RyYXRlcyBhIHRyZW5kIHdoZXJlIHRoZSBtb3N0IGNvbW1vbiByYXRpbmcgaXMgbGV2ZWwgMywgZm9sbG93ZWQgYnkgbGV2ZWwgNCwgbGV2ZWwgNSwgbGV2ZWwgMiwgbGV2ZWwgMSwgYW5kIGxldmVsIDYuIFRoaXMgZGlzdHJpYnV0aW9uIGluZGljYXRlcyB0aGF0IGEgc3Vic3RhbnRpYWwgcHJvcG9ydGlvbiBvZiBwYXJlbnRzIGJlbGlldmUgdGhlaXIgY2hpbGRyZW4gcG9zc2VzcyBncmFzcCBwb3dlciB0aGF0IGlzIGF2ZXJhZ2Ugb3Igc2xpZ2h0bHkgYWJvdmUgYXZlcmFnZSAobGV2ZWxzIDMgYW5kIDQpLCB3aXRoIGZld2VyIGNoaWxkcmVuIHJhdGVkIGF0IHRoZSBleHRyZW1lcyAobGV2ZWxzIDEsIDIsIDUsIGFuZCA2KS4NCg0KdmFyaWFibGVzIGRpc3RyaWJ1dGlvbiBvZiB0aGUgY2xhc3MgbGFiZWwgJ1ByZWRpY3RlZCBIb2JieSc6DQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKSAjIGluc3RhbGwgb25seSBvbmUgdGltZSB0aGVuIHB1dCB0aGlzIGNvbW1hbmQgYXMgY29tbWVudCBhZnRlciBpbnN0YWxsYXRpb24NCmxpYnJhcnkoZHBseXIpDQoNCmRhdGFzZXQyIDwtIEhvYmJ5X0RhdGEgJT4lIHNhbXBsZV9uKDE2MDApDQp0YWJsZShkYXRhc2V0MiRgUHJlZGljdGVkIEhvYmJ5YCkgJT4lIHBpZSgpDQp0YWIgPC0gZGF0YXNldDIkYFByZWRpY3RlZCBIb2JieWAgJT4lIHRhYmxlKCkNCnByZWNlbnRhZ2VzIDwtIHRhYiAlPiUgcHJvcC50YWJsZSgpICU+JSByb3VuZCgzKSAqIDEwMCANCnR4dCA8LSBwYXN0ZTAobmFtZXModGFiKSwgJ1xuJywgcHJlY2VudGFnZXMsICclJykNCnBpZSh0YWIsIGxhYmVscz10eHQpDQpgYGANCg0KVGhlIHBpZSBjaGFydCBpbGx1c3RyYXRlcyB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBjbGFzcyBsYWJlbCAnUHJlZGljdGVkIEhvYmJ5Jy4gSXQncyBldmlkZW50IHRoYXQgYSBzdWJzdGFudGlhbCBwb3J0aW9uLCBhcHByb3hpbWF0ZWx5IDQzLjclLCBvZiB0aGUgY2hpbGRyZW4ncyBob2JiaWVzIGFyZSBhY2FkZW1pYyBpbiBuYXR1cmUsIGluZGljYXRpbmcgYSBzdHJvbmcgaW50ZXJlc3QgaW4gZWR1Y2F0aW9uYWwgcHVyc3VpdHMuIEFkZGl0aW9uYWxseSwgMzAuOCUgb2YgdGhlIGtpZHMgYXJlIGVuZ2FnZWQgaW4gc3BvcnRzLCByZWZsZWN0aW5nIGEgc2lnbmlmaWNhbnQgaW5jbGluYXRpb24gdG93YXJkcyBwaHlzaWNhbCBhY3Rpdml0aWVzLiBBcnRzLXJlbGF0ZWQgaG9iYmllcyBhY2NvdW50IGZvciAyNS42JSBvZiB0aGUgdG90YWwsIHRoZSBkaXN0cmlidXRpb24gcmVmbGVjdHMgYSBoYXJtb25pb3VzIGJsZW5kIG9mIGhvYmJpZXMuIFRoaXMgYmFsYW5jZWQgZGlzdHJpYnV0aW9uIG5vdCBvbmx5IHNpZ25pZmllcyBhIHZhcmlldHkgb2YgaW50ZXJlc3RzIGJ1dCBhbHNvIGluZGljYXRlcyBhIHdlbGwtcm91bmRlZCBlbmdhZ2VtZW50IG9mIGNoaWxkcmVuIGluIGFjYWRlbWljLCBwaHlzaWNhbCwgYW5kIGNyZWF0aXZlIGFjdGl2aXRpZXMuDQoNCg0KKipTdGF0aXN0aWNhbCBtZWFzdXJlcyA6KioNCg0KYGBge3J9DQpmaW5kX21vZGUgPC0gZnVuY3Rpb24oeCkgew0KICB1IDwtIHVuaXF1ZSh4KQ0KICB0YWIgPC0gdGFidWxhdGUobWF0Y2goeCwgdSkpDQogIHVbdGFiID09IG1heCh0YWIpXQ0KfQ0KDQpmaW5kX21vZGUoSG9iYnlfRGF0YSRUaW1lX2FydCkNCiMgVXBkYXRlZCB1cHN0cmVhbQ0KaGlzdChIb2JieV9EYXRhJFRpbWVfYXJ0KQ0KDQojU3Rhc2hlZCBjaGFuZ2VzDQpgYGANCg0KVGhlIGhpc3RvZ3JhbSBtYWtlcyBpdCBldmlkZW50IHRoYXQgdGhlIG1vZGUgaXMgZXF1YWwgdG8gMSwgYW5kIHdlIGJlbGlldmUgdGhhdCB0aGUgaGlnaCBmcmVxdWVuY3kgb2YgcGFyZW50cyByYW5raW5nICIxIiBhcyB0aGUgbW9zdCBjaG9zZW4gcmFuayBpbiB0aGUgIlRpbWVfYXJ0IiB2YXJpYWJsZSBjb3VsZCBiZSBhdHRyaWJ1dGVkIHRvIHNldmVyYWwgZmFjdG9ycy4gSXQgbWlnaHQgaW5kaWNhdGUgdGhhdCBhIHNpZ25pZmljYW50IG51bWJlciBvZiBwYXJlbnRzIHBlcmNlaXZlIHRoZWlyIGNoaWxkcmVuJ3MgaW52b2x2ZW1lbnQgaW4gYXJ0IGFjdGl2aXRpZXMgYXMgcmVsYXRpdmVseSBsb3csIHBvc3NpYmx5IGR1ZSB0byB0aW1lIGNvbnN0cmFpbnRzLCBhY2FkZW1pYyBwcmlvcml0aWVzLCBvciBhIGxpbWl0ZWQgaW50ZXJlc3QgaW4gYXJ0LiBBbHRlcm5hdGl2ZWx5LCBpdCBjb3VsZCByZWZsZWN0IHRoYXQgcGFyZW50cyB2YWx1ZSBhIG1vcmUgYmFsYW5jZWQgYXBwcm9hY2ggdG8gdGhlaXIgY2hpbGRyZW4ncyBhY3Rpdml0aWVzLCB3aXRoIGEgdmFyaWV0eSBvZiBpbnRlcmVzdHMgYW5kIHJlc3BvbnNpYmlsaXRpZXMgc2hhcmluZyB0aGVpciB0aW1lLiBUaGlzIHRyZW5kIGNvdWxkIGFsc28gcmVzdWx0IGZyb20gYSBjdWx0dXJhbCBvciBlZHVjYXRpb25hbCBlbXBoYXNpcyBvbiBvdGhlciBzdWJqZWN0cyBhbmQgZXh0cmFjdXJyaWN1bGFyIGFjdGl2aXRpZXMgdGhhdCBjb21wZXRlIGZvciBhIGNoaWxkJ3MgdGltZSwgcG90ZW50aWFsbHkgbGVhZGluZyB0byBhIGxvd2VyIHJhbmtpbmcgZm9yIGFydC1yZWxhdGVkIGFjdGl2aXRpZXMuDQoNCmBgYHtyfQ0KZmluZF9tb2RlKEhvYmJ5X0RhdGEkR3Jhc3BfcG93KQ0KaGlzdChIb2JieV9EYXRhJEdyYXNwX3BvdykNCmBgYA0KDQpSYW5rIDMsIGluIHRoaXMgY29udGV4dCwgbWF5IGhhdmUgYmVlbiB0aGUgbW9zdCBjaG9zZW4gcmFuayBiZWNhdXNlIGl0IGxpa2VseSByZXByZXNlbnRzIGFuIGF2ZXJhZ2Ugb3IgbW9kZXJhdGUgbGV2ZWwgb2YgZ3Jhc3AgcG93ZXIuIFBhcmVudHMgbWF5IGhhdmUgYXNzZXNzZWQgdGhlaXIgY2hpbGRyZW4ncyBncmFzcCBwb3dlciBhcyBuZWl0aGVyIGV4Y2VwdGlvbmFsbHkgc3Ryb25nIChyYW5rIDUgb3IgNikgbm9yIHBhcnRpY3VsYXJseSB3ZWFrIChyYW5rIDEgb3IgMiksIHJlc3VsdGluZyBpbiB0aGUgcHJlZmVyZW5jZSBmb3IgdGhlIG1pZGRsZS1yYW5raW5nIG9wdGlvbi4gVGhpcyBjaG9pY2UgY291bGQgcmVmbGVjdCBhIHBlcmNlcHRpb24gdGhhdCB0aGVpciBjaGlsZHJlbidzIGdyYXNwIHBvd2VyIGZhbGxzIHdpdGhpbiBhIHR5cGljYWwgb3IgZXhwZWN0ZWQgcmFuZ2UsIG1ha2luZyBpdCB0aGUgbW9zdCBjb21tb24gcmF0aW5nLg0KDQpgYGB7cn0NCmZpbmRfbW9kZShIb2JieV9EYXRhJFRpbWVfc3BydCkNCmhpc3QoSG9iYnlfRGF0YSRUaW1lX3NwcnQpDQpgYGANCg0KQXMgc2hvd24gaW4gdGhlIGhpc3RvZ3JhbSB0aGUgcmFuayAzIG9uIGEgc2NhbGUgb2YgMSB0byA2IHdhcyBsaWtlbHkgdGhlIG1vc3QgY2hvc2VuIHJhbmsgZm9yIGFzc2Vzc2luZyBjaGlsZHJlbidzIGludm9sdmVtZW50IGluIHNwb3J0cyBiZWNhdXNlIGl0IHJlcHJlc2VudHMgYSBiYWxhbmNlZCBtaWRkbGUgZ3JvdW5kLiBQYXJlbnRzIG1heSBoYXZlIHBlcmNlaXZlZCByYW5rIDMgYXMgaW5kaWNhdGluZyB0aGF0IHRoZWlyIGNoaWxkcmVuIGFyZSBtb2RlcmF0ZWx5IGludm9sdmVkIGluIHNwb3J0cywgbm90IGV4Y2Vzc2l2ZWx5IGNvbW1pdHRlZCBvciBkaXNpbnRlcmVzdGVkLiBUaGlzIG1pZGRsZS1vZi10aGUtcm9hZCByYW5raW5nIHJlZmxlY3RzIGEgY29tbW9uIHBlcnNwZWN0aXZlIHRoYXQgbWFueSBwYXJlbnRzIG1heSBob2xkLCBjb25zaWRlcmluZyB0aGF0IGV4dHJlbWUgcmFua2luZ3MsIHN1Y2ggYXMgMSBvciA2LCBtaWdodCBzdWdnZXN0IGVpdGhlciBhIGxhY2sgb2YgaW52b2x2ZW1lbnQgb3IgYW4gZXhjZXNzaXZlIGZvY3VzIG9uIHNwb3J0cywgd2hpY2ggbWF5IG5vdCBhbGlnbiB3aXRoIHRoZWlyIHBlcmNlcHRpb24gb2YgdGhlaXIgY2hpbGQncyBvdmVyYWxsIHdlbGwtcm91bmRlZCBkZXZlbG9wbWVudC4NCg0KVGhlIGhpc3RvZ3JhbSBncmFwaCBmb3IgIlRpbWVfYXJ0IiB2YXJpYWJsZQ0KYGBge3J9DQpoaXN0KEhvYmJ5X0RhdGEkVGltZV9hcnQpDQpgYGANCg0KVGhlIGhpc3RvZ3JhbSBtYWtlcyBpdCBldmlkZW50IHRoYXQgdGhlIG1vZGUgaXMgZXF1YWwgdG8gMSwgYW5kIHdlIGJlbGlldmUgdGhhdCB0aGUgaGlnaCBmcmVxdWVuY3kgb2YgcGFyZW50cyByYW5raW5nICIxIiBhcyB0aGUgbW9zdCBjaG9zZW4gcmFuayBpbiB0aGUgIlRpbWVfYXJ0IiB2YXJpYWJsZSBjb3VsZCBiZSBhdHRyaWJ1dGVkIHRvIHNldmVyYWwgZmFjdG9ycy4gSXQgbWlnaHQgaW5kaWNhdGUgdGhhdCBhIHNpZ25pZmljYW50IG51bWJlciBvZiBwYXJlbnRzIHBlcmNlaXZlIHRoZWlyIGNoaWxkcmVuJ3MgaW52b2x2ZW1lbnQgaW4gYXJ0IGFjdGl2aXRpZXMgYXMgcmVsYXRpdmVseSBsb3csIHBvc3NpYmx5IGR1ZSB0byB0aW1lIGNvbnN0cmFpbnRzLCBhY2FkZW1pYyBwcmlvcml0aWVzLCBvciBhIGxpbWl0ZWQgaW50ZXJlc3QgaW4gYXJ0LiBBbHRlcm5hdGl2ZWx5LCBpdCBjb3VsZCByZWZsZWN0IHRoYXQgcGFyZW50cyB2YWx1ZSBhIG1vcmUgYmFsYW5jZWQgYXBwcm9hY2ggdG8gdGhlaXIgY2hpbGRyZW4ncyBhY3Rpdml0aWVzLCB3aXRoIGEgdmFyaWV0eSBvZiBpbnRlcmVzdHMgYW5kIHJlc3BvbnNpYmlsaXRpZXMgc2hhcmluZyB0aGVpciB0aW1lLiBUaGlzIHRyZW5kIGNvdWxkIGFsc28gcmVzdWx0IGZyb20gYSBjdWx0dXJhbCBvciBlZHVjYXRpb25hbCBlbXBoYXNpcyBvbiBvdGhlciBzdWJqZWN0cyBhbmQgZXh0cmFjdXJyaWN1bGFyIGFjdGl2aXRpZXMgdGhhdCBjb21wZXRlIGZvciBhIGNoaWxkJ3MgdGltZSwgcG90ZW50aWFsbHkgbGVhZGluZyB0byBhIGxvd2VyIHJhbmtpbmcgZm9yIGFydC1yZWxhdGVkIGFjdGl2aXRpZXMuDQoNCg0KYGBge3J9DQpmaW5kX21vZGUoSG9iYnlfRGF0YSRHcmFzcF9wb3cpDQpgYGANClRoZSBoaXN0b2dyYW0gZ3JhcGggZm9yICJHcmFzcF9wb3ciIHZhcmlhYmxlDQpgYGB7cn0NCmhpc3QoSG9iYnlfRGF0YSRHcmFzcF9wb3cpDQpgYGANClJhbmsgMywgaW4gdGhpcyBjb250ZXh0LCBtYXkgaGF2ZSBiZWVuIHRoZSBtb3N0IGNob3NlbiByYW5rIGJlY2F1c2UgaXQgbGlrZWx5IHJlcHJlc2VudHMgYW4gYXZlcmFnZSBvciBtb2RlcmF0ZSBsZXZlbCBvZiBncmFzcCBwb3dlci4gUGFyZW50cyBtYXkgaGF2ZSBhc3Nlc3NlZCB0aGVpciBjaGlsZHJlbidzIGdyYXNwIHBvd2VyIGFzIG5laXRoZXIgZXhjZXB0aW9uYWxseSBzdHJvbmcgKHJhbmsgNSBvciA2KSBub3IgcGFydGljdWxhcmx5IHdlYWsgKHJhbmsgMSBvciAyKSwgcmVzdWx0aW5nIGluIHRoZSBwcmVmZXJlbmNlIGZvciB0aGUgbWlkZGxlLXJhbmtpbmcgb3B0aW9uLiBUaGlzIGNob2ljZSBjb3VsZCByZWZsZWN0IGEgcGVyY2VwdGlvbiB0aGF0IHRoZWlyIGNoaWxkcmVuJ3MgZ3Jhc3AgcG93ZXIgZmFsbHMgd2l0aGluIGEgdHlwaWNhbCBvciBleHBlY3RlZCByYW5nZSwgbWFraW5nIGl0IHRoZSBtb3N0IGNvbW1vbiByYXRpbmcuDQoNCg0KYGBge3J9DQpmaW5kX21vZGUoSG9iYnlfRGF0YSRUaW1lX3NwcnQpDQpgYGANCg0KVGhlIGhpc3RvZ3JhbSBncmFwaCBmb3IgIlRpbWVfc3BydCIgdmFyaWFibGUNCmBgYHtyfQ0KaGlzdChIb2JieV9EYXRhJFRpbWVfc3BydCkNCmBgYA0KQXMgc2hvd24gaW4gdGhlIGhpc3RvZ3JhbSB0aGUgcmFuayAzIG9uIGEgc2NhbGUgb2YgMSB0byA2IHdhcyBsaWtlbHkgdGhlIG1vc3QgY2hvc2VuIHJhbmsgZm9yIGFzc2Vzc2luZyBjaGlsZHJlbidzIGludm9sdmVtZW50IGluIHNwb3J0cyBiZWNhdXNlIGl0IHJlcHJlc2VudHMgYSBiYWxhbmNlZCBtaWRkbGUgZ3JvdW5kLiBQYXJlbnRzIG1heSBoYXZlIHBlcmNlaXZlZCByYW5rIDMgYXMgaW5kaWNhdGluZyB0aGF0IHRoZWlyIGNoaWxkcmVuIGFyZSBtb2RlcmF0ZWx5IGludm9sdmVkIGluIHNwb3J0cywgbm90IGV4Y2Vzc2l2ZWx5IGNvbW1pdHRlZCBvciBkaXNpbnRlcmVzdGVkLiBUaGlzIG1pZGRsZS1vZi10aGUtcm9hZCByYW5raW5nIHJlZmxlY3RzIGEgY29tbW9uIHBlcnNwZWN0aXZlIHRoYXQgbWFueSBwYXJlbnRzIG1heSBob2xkLCBjb25zaWRlcmluZyB0aGF0IGV4dHJlbWUgcmFua2luZ3MsIHN1Y2ggYXMgMSBvciA2LCBtaWdodCBzdWdnZXN0IGVpdGhlciBhIGxhY2sgb2YgaW52b2x2ZW1lbnQgb3IgYW4gZXhjZXNzaXZlIGZvY3VzIG9uIHNwb3J0cywgd2hpY2ggbWF5IG5vdCBhbGlnbiB3aXRoIHRoZWlyIHBlcmNlcHRpb24gb2YgdGhlaXIgY2hpbGQncyBvdmVyYWxsIHdlbGwtcm91bmRlZCBkZXZlbG9wbWVudC4NCg0KDQoqKkRhdGEgcHJlcHJvY2Vzc2luZyoqDQoNCg0KKiojMSMgRGF0YSBjbGVhbmluZzoqKg0KDQpEdXJpbmcgdGhlIGRhdGEgY2xlYW5pbmcgc3RhZ2UsIGZpbmRpbmcgYW5kIGZpeGluZyBmYXVsdHMsIGluY29uc2lzdGVuY2llcywgYW5kIGVycm9ycyBpbiBhIGRhdGFzZXQgaGVscHMgaXQgYmUgbW9yZSByZWxpYWJsZSBhbmQgb2YgaGlnaGVyIHF1YWxpdHkgZm9yIGFuYWx5c2lzIGFuZCBtb2RlbGluZy4gVGhlcmUgYXJlIG1ldGhvZHMgZm9yIGhhbmRsaW5nIG1pc3NpbmcgdmFsdWVzLCBkZXRlY3Rpbmcgb3V0bGllcnMsIHJlc29sdmluZyBpbmNvbnNpc3RlbmNpZXMsIGFuZCBzdGFuZGFyZGl6aW5nIGZvcm1hdHMuDQoNCmltcG9ydCBEYXRhc2V0IkhvYmJ5X0RhdGEiDQoNCmBgYHtyfQ0KDQpWaWV3KEhvYmJ5X0RhdGEpDQoNCnN0cihIb2JieV9EYXRhKQ0KYGBgDQoNCmNoZWNrIG1pc3NpbmcgdmFsdWUgOg0KDQpgYGB7cn0NCmlzLm5hKEhvYmJ5X0RhdGEpDQpgYGANCg0KZmluZCB0aGUgdG90YWwgbnVsbCB2YWx1ZXMgaW4gdGhlIGRhdGFzZXQ6DQoNCmBgYHtyfQ0Kc3VtKGlzLm5hKEhvYmJ5X0RhdGEpKQ0KYGBgDQoNCkRlc2NyaXB0aW9uOg0KDQpUaGlzIHN0YWdlIGludm9sdmVzIGNoZWNraW5nIGFuZCBkZWxldGluZyBmb3IgbnVsbCBhbmQgbWlzc2luZyB2YWx1ZXMgYmVjYXVzZSB0aGV5IG1pZ2h0IGhhdmUgYSBzaWduaWZpY2FudCBpbXBhY3Qgb24gdGhlIGRhdGEgYW5kIGNhdXNlIGVycm9ycyBhbmQgbmVnYXRpdmUgZWZmZWN0cyBpbiBzdWJzZXF1ZW50IHN0ZXBzLldlIHNpbXBseSBsb29rZWQgZm9yIG1pc3NpbmcgdmFsdWVzLCBhbmQgdGhlcmUgYXJlIGEgbWlzc2luZyB2YWx1ZXMgaW4gb3VyIGRhdGFzZXQuIEFjY29yZGluZyB0byBvdXIgaW52ZXN0aWdhdGlvbiwgdGhlIGRhdGFzZXQgZG9lcyBub3QgY29udGFpbiBhbnkgb3V0bGllcnMgc2luY2UgaXQgZG9lc24ndCBoYXZlIGEgbnVtZXJpY2FsIGRhdGEgdHlwZS4gQWRkaXRpb25hbGx5LCB0aGVyZSBhcmUgbm8gaW5jb25zaXN0ZW50IHZhbHVlcyBvciBvdGhlciBlcnJvcnMuDQoNCg0KRGF0YXNldCBhZnRlciBjbGVhbmluZyBzdGVwOg0KYGBge3J9DQpzYW1wbGUoSG9iYnlfRGF0YSkNCmBgYA0KDQoqKiMyI0VuY29kaW5nOioqDQpUaGlzIHN0ZXAgaW5jbHVkZXMgYSBDb252ZXJ0aW5nIGNhdGVnb3JpY2FsIG9yIG5vbi1udW1lcmljIGRhdGEgaW50byBhIG51bWVyaWNhbCBmb3JtYXQsIHdoaWNoIGlzIG5lY2Vzc2FyeSBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIHN1YnNlcXVlbnQgc3RlcHMgaW4gcHJlcHJvY2Vzc2luZy4NCg0KYGBge3J9DQpIb2JieV9EYXRhJE9seW1waWFkX1BhcnRpY2lwYXRpb24gPSBmYWN0b3IoSG9iYnlfRGF0YSRPbHltcGlhZF9QYXJ0aWNpcGF0aW9uLGxldmVscyA9IGMoIk5vIiwgIlllcyIpLCBsYWJlbHMgPSBjKDAsIDEpKQ0KSG9iYnlfRGF0YSRTY2hvbGFyc2hpcCA9IGZhY3RvcihIb2JieV9EYXRhJFNjaG9sYXJzaGlwICwgbGV2ZWxzID0gYygiTm8iLCAiWWVzIiksIGxhYmVscyA9IGMoMCwgMSkpDQpIb2JieV9EYXRhJFNjaG9vbCA9IGZhY3RvcihIb2JieV9EYXRhJFNjaG9vbCwgbGV2ZWxzID0gYygiTm8iLCAiWWVzIiksIGxhYmVscyA9IGMoMCwgMSkpDQpIb2JieV9EYXRhJFByb2plY3RzID0gZmFjdG9yKEhvYmJ5X0RhdGEkUHJvamVjdHMsIGxldmVscyA9IGMoIk5vIiwgIlllcyIpLCBsYWJlbHMgPSBjKDAsIDEpKQ0KSG9iYnlfRGF0YSRNZWRhbHMgPSBmYWN0b3IoSG9iYnlfRGF0YSRNZWRhbHMsIGxldmVscyA9IGMoIk5vIiwgIlllcyIpLCBsYWJlbHMgPSBjKDAsIDEpKQ0KSG9iYnlfRGF0YSRDYXJlZXJfc3BydCA9IGZhY3RvcihIb2JieV9EYXRhJENhcmVlcl9zcHJ0LCBsZXZlbHMgPSBjKCJObyIsICJZZXMiKSwgbGFiZWxzID0gYygwLCAxKSkNCkhvYmJ5X0RhdGEkQWN0X3NwcnQgPSBmYWN0b3IoSG9iYnlfRGF0YSRBY3Rfc3BydCwgbGV2ZWxzID0gYygiTm8iLCAiWWVzIiksIGxhYmVscyA9IGMoMCwgMSkpDQpIb2JieV9EYXRhJEZhbnRfYXJ0cyA9IGZhY3RvcihIb2JieV9EYXRhJEZhbnRfYXJ0cywgbGV2ZWxzID0gYygiTm8iLCAiWWVzIiksIGxhYmVscyA9IGMoMCwgMSkpDQpIb2JieV9EYXRhJFdvbl9hcnRzID0gZmFjdG9yKEhvYmJ5X0RhdGEkV29uX2FydHMsIGxldmVscyA9IGMoIk5vIiwgIk1heWJlIiwgIlllcyIpLCBsYWJlbHMgPSBjKDAsIDIsIDEpKQ0KSG9iYnlfRGF0YSRGYXZfc3ViID0gZmFjdG9yKEhvYmJ5X0RhdGEkRmF2X3N1YiwgbGV2ZWxzID0gYygiU2NpZW5jZSIsICJNYXRoZW1hdGljcyIsICJIaXN0b3J5L0dlb2dyYXBoeSIsICJBbnkgbGFuZ3VhZ2UiKSwgbGFiZWxzID0gYygxLCAyLCAzLCA0KSkNCkhvYmJ5X0RhdGEkYFByZWRpY3RlZCBIb2JieWAgPC0gZmFjdG9yKEhvYmJ5X0RhdGEkYFByZWRpY3RlZCBIb2JieWAsIGxldmVscyA9IGMoIkFjYWRlbWljcyIsICJBcnRzIiwgIlNwb3J0cyIpLCBsYWJlbHMgPSBjKDEsIDIsIDMpKQ0KYGBgDQoNCkRhdGFzZXQgYWZ0ZXIgRW5jb2RpbmcgOg0KYGBge3J9DQpzYW1wbGUoSG9iYnlfRGF0YSkNCmBgYA0KDQoNCioqIzMjIE5vcm1hbGl6YXRpb24gYW5kIERpc2NldGl6YXRpb246KioNCg0KV2UgZG9uJ3QgbmVlZCB0byB1c2Ugbm9ybWFsaXphdGlvbiBhbmQgZGlzY2V0aXphdGlvbiBpbiBvdXIgZGF0YXNldC4gU2luY2Ugb3VyIGRhdGFzZXQgZG9lc24ndCBoYXZlIG51bWVyaWMgYXR0cmlidXRlcyBhbmQgbm9ybWFsaXphdGlvbiBpbnZvbHZlcyBtYXRoZW1hdGljYWwgb3BlcmF0aW9ucywgd2hpY2ggY2FuIHJlc3VsdCBpbiBtZWFuaW5nbGVzcyB2YWx1ZXMgYW5kIGVycm9ycywgQWxzbywgYXBwbHlpbmcgZGlzY3JldGl6YXRpb24gbGVhZHMgdG8gYSBsb3NzIG9mIGluZm9ybWF0aW9uIGFuZCBjcmVhdGVzIGludGVydmFscyBhbmQgcmVsYXRpb25zaGlwcyB0aGF0IGRvbid0IGV4aXN0IGJldHdlZW4gdmFsdWVzLg0KDQoqKiM0I0ZlYXR1cmUgU2VsZWN0aW9uOioqDQoNClRvIGltcHJvdmUgdGhlIGFjY3VyYWN5IG9mIG91ciBwcmVkaWN0aW9ucyBmb3IgdGhlIHRhcmdldCBjbGFzcyAiUHJlZGljdGVkIEhvYmJ5IiBhbmQgZGVjcmVhc2UgdGhlIHByb2Nlc3NpbmcgdGltZSBvZiBvdXIgY2xhc3NpZmllciwgd2Ugd2lsbCB1dGlsaXplIGZlYXR1cmUgc2VsZWN0aW9uIHRlY2huaXF1ZXMuIFRoZXNlIHRlY2huaXF1ZXMgZW5hYmxlIHVzIHRvIGVsaW1pbmF0ZSByZWR1bmRhbnQgb3IgaXJyZWxldmFudCBhdHRyaWJ1dGVzIGZyb20gdGhlIGRhdGFzZXQsIHJlc3VsdGluZyBpbiBhIG1vcmUgY29uY2lzZSBzdWJzZXQgb2YgZmVhdHVyZXMgdGhhdCBwcm92aWRlIHRoZSBtb3N0IHZhbHVhYmxlIGluZm9ybWF0aW9uIGZvciBvdXIgcHJlZGljdGlvbnMuDQoNClNwZWNpZmljYWxseSwgd2Ugd2lsbCB1c2UgdHdvIGZlYXR1cmUgc2VsZWN0aW9uIG1ldGhvZHM6IFJhbmsgRmVhdHVyZXMgYnkgSW1wb3J0YW5jZSBhbmQgRmVhdHVyZSBTZWxlY3Rpb24gVXNpbmcgUmVjdXJzaXZlIEZlYXR1cmUgRWxpbWluYXRpb24gKFJGRSkuDQoNCjEuKipSYW5rIEZlYXR1cmVzIGJ5IEltcG9ydGFuY2U6KioNCg0KVGhpcyBtZXRob2QgdXNlZCBpbiB0aGUgY29kZSBoZWxwcyB1cyBkZXRlcm1pbmUgd2hpY2ggZmVhdHVyZXMgYXJlIG1vc3QgaW1wb3J0YW50IGZvciBwcmVkaWN0aW5nIHRoZSAiUHJlZGljdGVkIEhvYmJ5IiBjbGFzcyBsYWJlbCBpbiB0aGUgZGF0YXNldC4gSXQgdXRpbGl6ZXMgdGhlIFJhbmRvbSBGb3Jlc3QgYWxnb3JpdGhtLCBrbm93biBmb3IgaXRzIGFjY3VyYXRlIHByZWRpY3Rpb24gY2FwYWJpbGl0aWVzLiBUaGlzIG1ldGhvZCBjYWxjdWxhdGVzIHRoZSBpbXBvcnRhbmNlIG9mIGVhY2ggZmVhdHVyZSBieSBhc3Nlc3NpbmcgaXRzIGNvbnRyaWJ1dGlvbiB0byB0aGUgb3ZlcmFsbCBhY2N1cmFjeSBvZiB0aGUgcHJlZGljdGlvbnMuIEJ5IHJhbmtpbmcgdGhlIGZlYXR1cmVzIGJhc2VkIG9uIHRoZWlyIGltcG9ydGFuY2UsIHdlIGNhbiBpZGVudGlmeSB0aGUgb25lcyB0aGF0IGhhdmUgdGhlIGdyZWF0ZXN0IGluZmx1ZW5jZSBvbiBkZXRlcm1pbmluZyBob2JiaWVzLg0KDQpFbnN1cmUgdGhlIHJlc3VsdHMgYXJlIHJlcGVhdGFibGUgYnkgc2V0dGluZyBhIHNlZWQ6DQoNCmBgYHtyfQ0Kc2V0LnNlZWQoNykNCg0KYGBgDQoNCkxvYWQgdGhlIG5lY2Vzc2FyeSBsaWJyYXJpZXM6DQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQ0KaW5zdGFsbC5wYWNrYWdlcygicmFuZG9tRm9yZXN0IikNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkNCmBgYA0KDQpTZXBhcmF0ZSB0aGUgcHJlZGljdG9ycyBhbmQgdGhlIGNsYXNzIGxhYmVsOg0KDQpgYGB7cn0NCnByZWRpY3RvcnMgPC0gSG9iYnlfRGF0YVssIC0xNF0gICMgRXhjbHVkaW5nIHRoZSBjbGFzcyBsYWJlbCAoUHJlZGljdGVkIEhvYmJ5KQ0KY2xhc3NfbGFiZWwgPC0gSG9iYnlfRGF0YSRgUHJlZGljdGVkIEhvYmJ5YA0KYGBgDQoNClRyYWluIGEgUmFuZG9tIEZvcmVzdCBtb2RlbDoNCg0KYGBge3J9DQptb2RlbCA8LSByYW5kb21Gb3Jlc3QocHJlZGljdG9ycywgY2xhc3NfbGFiZWwsIGltcG9ydGFuY2UgPSBUUlVFKQ0KYGBgDQoNCkdldCB0aGUgdmFyaWFibGUgaW1wb3J0YW5jZToNCg0KYGBge3J9DQppbXBvcnRhbmNlIDwtIGltcG9ydGFuY2UobW9kZWwpDQpgYGANCg0KUmFuayB0aGUgZmVhdHVyZXMgYnkgaW1wb3J0YW5jZToNCg0KYGBge3J9DQpyYW5rZWRfZmVhdHVyZXMgPC0gc29ydChpbXBvcnRhbmNlWywgIk1lYW5EZWNyZWFzZUdpbmkiXSwgZGVjcmVhc2luZyA9IFRSVUUpDQpgYGANCg0KUHJpbnQgdGhlIHJhbmtlZCBmZWF0dXJlczoNCg0KYGBge3J9DQpwcmludChyYW5rZWRfZmVhdHVyZXMpDQoNCmJhcnBsb3QocmFua2VkX2ZlYXR1cmVzLCBob3JpeiA9IFRSVUUsIGxhcyA9IDEsIG1haW4gPSAiS2lkcyBIb2JieSBWYXJpYWJsZSBJbXBvcnRhbmNlIFJhbmtpbmciKQ0KYGBgDQoNCjIuKipGZWF0dXJlIFNlbGVjdGlvbiBVc2luZyBSRkU6KioNCg0KVGhlIFJlY3Vyc2l2ZSBGZWF0dXJlIEVsaW1pbmF0aW9uIChSRkUpIG1ldGhvZCB3aXRoIFJhbmRvbSBGb3Jlc3QgaXMgYSB0ZWNobmlxdWUgdXNlZCB0byBzZWxlY3QgdGhlIG1vc3QgaW1wb3J0YW50IGZlYXR1cmVzIGZvciBhY2N1cmF0ZSBwcmVkaWN0aW9ucyAuIEl0IGl0ZXJhdGl2ZWx5IGVsaW1pbmF0ZXMgbGVzcyByZWxldmFudCBmZWF0dXJlcywgcmV0cmFpbmluZyB0aGUgbW9kZWwgYXQgZWFjaCBzdGVwIHRvIGV2YWx1YXRlIHBlcmZvcm1hbmNlLiBCeSBmb2N1c2luZyBvbiB0aGUgbW9zdCBpbmZvcm1hdGl2ZSBmZWF0dXJlcywgUkZFIGltcHJvdmVzIHJlZHVjZXMgY29tcGxleGl0eSwgYW5kIGVuaGFuY2VzIHByZWRpY3Rpb24gYWNjdXJhY3kuIEl0IGlzIHBhcnRpY3VsYXJseSBlZmZlY3RpdmUgd2l0aCBSYW5kb20gRm9yZXN0IGR1ZSB0byBpdHMgYWJpbGl0eSB0byBoYW5kbGUgY29tcGxleCByZWxhdGlvbnNoaXBzIGFuZCBoaWdoLWRpbWVuc2lvbmFsIGRhdGEuIFJGRSBoZWxwcyBpZGVudGlmeSB0aGUgbW9zdCBpbXBvcnRhbnQgYXR0cmlidXRlcyBhc3NvY2lhdGVkIHdpdGggdGhlIHRhcmdldCB2YXJpYWJsZSwgZW5hYmxpbmcgdGhlIGNyZWF0aW9uIG9mIG1vcmUgZWZmaWNpZW50IGFuZCBhY2N1cmF0ZSBtb2RlbHMuDQoNCkVuc3VyZSB0aGUgcmVzdWx0cyBhcmUgcmVwZWF0YWJsZSBieSBzZXR0aW5nIGEgc2VlZDoNCg0KYGBge3J9DQpzZXQuc2VlZCg3KQ0KYGBgDQoNCkxvYWQgdGhlIG5lY2Vzc2FyeSBsaWJyYXJpZXM6DQoNCmBgYHtyfQ0KbGlicmFyeShjYXJldCkNCmBgYA0KDQpEZWZpbmUgdGhlIGNvbnRyb2wgcGFyYW1ldGVycyBmb3IgUkZFIHVzaW5nIHJhbmRvbSBmb3Jlc3Qgc2VsZWN0aW9uIGZ1bmN0aW9uOg0KDQpgYGB7cn0NCmNvbnRyb2wgPC0gcmZlQ29udHJvbChmdW5jdGlvbnMgPSByZkZ1bmNzLCBtZXRob2QgPSAiY3YiLCBudW1iZXIgPSAxMCkNCmBgYA0KDQpFeHRyYWN0IHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzIGZyb20gSG9iYnlfRGF0YToNCg0KYGBge3J9DQpwcmVkaWN0b3JzIDwtIEhvYmJ5X0RhdGFbLCAtbmNvbChIb2JieV9EYXRhKV0NCmBgYA0KDQpDb252ZXJ0IHRoZSBvdXRjb21lIHZhcmlhYmxlIHRvIGEgZmFjdG9yOg0KDQpgYGB7cn0NCm91dGNvbWUgPC0gYXMuZmFjdG9yKEhvYmJ5X0RhdGEkYFByZWRpY3RlZCBIb2JieWApDQpgYGANCg0KUnVuIHRoZSBSRkUgYWxnb3JpdGhtOg0KDQpgYGB7cn0NCnJlc3VsdHMgPC0gcmZlKHByZWRpY3RvcnMsIG91dGNvbWUsIHNpemVzID0gMTpuY29sKEhvYmJ5X0RhdGEpLCByZmVDb250cm9sID0gY29udHJvbCkNCmBgYA0KDQpTdW1tYXJpemUgdGhlIHJlc3VsdHM6DQoNCmBgYHtyfQ0KcHJpbnQocmVzdWx0cykNCmBgYA0KDQpMaXN0IHRoZSBjaG9zZW4gZmVhdHVyZXMgc2VsZWN0ZWQgYnkgUkZFOg0KDQpgYGB7cn0NCnByZWRpY3RvcnMocmVzdWx0cykNCmBgYA0KDQpQbG90IHRoZSByZXN1bHRzOg0KDQpgYGB7cn0NCnBsb3QocmVzdWx0cywgdHlwZSA9IGMoImciLCAibyIpKQ0KYGBgDQoNCjMuKipSZW1vdmluZyBJcnJlbGV2YW50IENvbHVtbnM6KioNCg0KQnkgY29uc2lkZXJpbmcgYm90aCBSZWN1cnNpdmUgRmVhdHVyZSBFbGltaW5hdGlvbiAoUkZFKSBhbmQgUmFuayBCeSBJbXBvcnRhbmNlLCB3ZSBjYW4gbWFrZSBpbmZvcm1lZCBkZWNpc2lvbnMgYWJvdXQgZmVhdHVyZSByZWxldmFuY2UgYW5kIGltcGFjdCBvbiB0aGUgbW9kZWwuIEluIHRoaXMgY2FzZSwgdGhlIGNvbHVtbnMgIlNjaG9vbCwiICJNZWRhbHMiIHNob3VsZCBiZSBkZWxldGVkIGFzIHRoZXkgaGF2ZSBsb3dlciBpbXBvcnRhbmNlIHNjb3JlcyBjb21wYXJlZCB0byB0aGUgc2VsZWN0ZWQgdmFyaWFibGVzLiBSZW1vdmluZyB0aGVzZSBjb2x1bW5zIHNpbXBsaWZpZXMgdGhlIG1vZGVsIGFuZCByZWR1Y2VzIGRpbWVuc2lvbmFsaXR5LCBlbGltaW5hdGluZyBwb3RlbnRpYWwgbm9pc2UgYW5kIGlycmVsZXZhbnQgaW5mb3JtYXRpb24gdGhhdCBjb3VsZCBoaW5kZXIgYWNjdXJhdGUgcHJlZGljdGlvbnMuDQoNClJlbW92ZSB0aGUgc3BlY2lmaWVkIGNvbHVtbnMgZnJvbSB0aGUgSG9iYnlfS2lkcyBkYXRhc2V0Og0KDQpgYGB7cn0NCkhvYmJ5X0RhdGEgPC0gSG9iYnlfRGF0YVssICEoY29sbmFtZXMoSG9iYnlfRGF0YSkgJWluJSBjKCJTY2hvb2wiLCAiTWVkYWxzIikpXQ0KYGBgDQoNCkRpc3BsYXkgdGhlIHVwZGF0ZWQgZGF0YXNldCBhZnRlciBkZWxldGluZyBjb2x1bW5zOg0KDQpgYGB7cn0NCnNhbXBsZShIb2JieV9EYXRhKQ0KYGBgDQp3ZSBjYW4gc2VlIHRoYXQgdGhlIHR3byBjb2x1bW5zIGFyZSBkZWxldGVkKCJTY2hvb2wiLCAiTWVkYWxzIikuDQoNCg0KDQoNCioqQmFsYW5jZWQgRGF0YSoqDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIGNsYXNzIGltYmFsYW5jZQ0KY2xhc3NfaW1iYWxhbmNlIDwtIG1heChwcm9wLnRhYmxlKHRhYmxlKEhvYmJ5X0RhdGEkYFByZWRpY3RlZCBIb2JieWApKSkgLSBtaW4ocHJvcC50YWJsZSh0YWJsZShIb2JieV9EYXRhJGBQcmVkaWN0ZWQgSG9iYnlgKSkpDQoNCiMgUHJpbnQgdGhlIHJlc3VsdA0KcHJpbnQoY2xhc3NfaW1iYWxhbmNlKQ0KYGBgDQppZiBjbGFzc19pbWJhbGFuY2UgaXMgY2xvc2UgdG8gMCwgaXQgc3VnZ2VzdHMgdGhhdCB0aGUgcHJvcG9ydGlvbnMgb2YgZGlmZmVyZW50IGNsYXNzZXMgYXJlIHJlbGF0aXZlbHkgc2ltaWxhciwgaW5kaWNhdGluZyBhIGJhbGFuY2VkIGRhdGFzZXQuIENvbnZlcnNlbHksIGlmIGNsYXNzX2ltYmFsYW5jZSBpcyBsYXJnZXIsIGl0IHN1Z2dlc3RzIGEgbW9yZSBzaWduaWZpY2FudCBpbWJhbGFuY2UgYmV0d2VlbiB0aGUgY2xhc3Nlcy4NClRoZSBjYWxjdWxhdGVkIGNsYXNzIGltYmFsYW5jZSB2YWx1ZSBvZiAwLjE4MDUxMjIgc3VnZ2VzdHMgdGhhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGNsYXNzZXMgaW4gdGhlICJQcmVkaWN0ZWRfSG9iYnkiIGNvbHVtbiBvZiB0aGUgZGF0YXNldCBpcyByZWxhdGl2ZWx5IGJhbGFuY2VkLiBUaGUgY2xhc3MgaW1iYWxhbmNlIGlzIGEgbWVhc3VyZSBvZiB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBwcm9wb3J0aW9ucyBvZiB0aGUgbW9zdCBwcmV2YWxlbnQgYW5kIGxlYXN0IHByZXZhbGVudCBjbGFzc2VzLiBJbiB0aGlzIGNhc2UsIHRoZSB2YWx1ZSBpcyBjbG9zZSB0byAwLCBpbmRpY2F0aW5nIHRoYXQgdGhlcmUgaXMgYSBtaW5pbWFsIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcHJvcG9ydGlvbnMgb2YgZGlmZmVyZW50IGNsYXNzZXMuIEEgbG93ZXItY2xhc3MgaW1iYWxhbmNlIHZhbHVlIGlzIGdlbmVyYWxseSBkZXNpcmFibGUsIGFzIGl0IHNpZ25pZmllcyBhIG1vcmUgZXZlbiBkaXN0cmlidXRpb24gb2YgaW5zdGFuY2VzIGFjcm9zcyBjbGFzc2VzLCB3aGljaCBjYW4gYmUgYmVuZWZpY2lhbCBmb3IgdGhlIHRyYWluaW5nIGFuZCBwZXJmb3JtYW5jZSBvZiBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscy4NCg0KDQoqKkRhdGEgTWluaW5nIFRlY2huaXF1ZSoqDQoNCg0KKipFdmFsdWF0aW9uIEFuZCBDb21wYXJpc29uKioNCg0KKiotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gQ2xhc3NpZmljYXRpb24tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKioNCg0KQWZ0ZXIgcHJlcHJvY2Vzc2luZywgd2Ugd2lsbCBwcm9jZWVkIHRvIHRoZSBjbGFzc2lmaWNhdGlvbiBzdGVwLiBJbiB0aGlzIHBoYXNlLCBhcyBwYXJ0IG9mIHN1cGVydmlzZWQgbGVhcm5pbmcsIHdlIHdpbGwgYXBwbHkgYSBjbGFzc2lmaWNhdGlvbiBhbGdvcml0aG0gdG8gYXNzaWduIGVhY2ggZGF0YSBwb2ludCBpbnRvIHByZWRlZmluZWQgY2F0ZWdvcmllcyBiYXNlZCBvbiBpdHMgYXR0cmlidXRlcy4gVGhpcyBpbnZvbHZlcyBzZWxlY3RpbmcgdGhlIG1vc3QgcmVsZXZhbnQgZmVhdHVyZXMgdGhhdCBoYXZlIGJlZW4gY2xlYW5lZCBhbmQgZm9ybWF0dGVkIGR1cmluZyBwcmVwcm9jZXNzaW5nLiBUaGUgc2VsZWN0ZWQgbW9kZWwgd2lsbCB0aGVuIGxlYXJuIGZyb20gdHJhaW5pbmcgZGF0YSwgZW5hYmxpbmcgaXQgdG8gcHJlZGljdCB0aGUgY2F0ZWdvcnkgb2YgbmV3LCB1bnNlZW4gZGF0YSBhY2N1cmF0ZWx5LiBUaGlzIHN0ZXAgaXMgZXNzZW50aWFsIGZvciBtYWtpbmcgaW5mb3JtZWQgZGVjaXNpb25zIG9yIHByZWRpY3Rpb25zIGJhc2VkIG9uIHRoZSBkYXRhLg0KDQpXZSBpbXBsZW1lbnQgYSBkZWNpc2lvbiB0cmVlIG9uIHRoZSBkYXRhc2V0LCB3aGljaCBoYXMgYmVlbiBwYXJ0aXRpb25lZCBpbnRvIFRyYWluaW5nIGFuZCBUZXN0IHNldHMgdXNpbmcgdGhlIHBlcmNlbnRhZ2Ugc3BsaXQgbWV0aG9kLiBUaGlzIG1ldGhvZCBlbnN1cmVzIHRoYXQgZWFjaCBzdWJzZXQgaXMgYSByYW5kb21pemVkLCByZXByZXNlbnRhdGl2ZSBzYW1wbGUgb2YgdGhlIGVudGlyZSBkYXRhc2V0LCB0aHVzIG1pbmltaXppbmcgYmlhcyBhbmQgZW5hYmxpbmcgY29uc2lzdGVudCBtb2RlbCBwZXJmb3JtYW5jZSBldmFsdWF0aW9uLiBXZSBjaG9vc2UgdGhyZWUgZGlmZmVyZW50IHNwbGl0IHNpemVzOiAoIjkwJSIsICIxMCUiKSwgKCI4MCUiLCAiMjAlIiksIGFuZCAoIjcwJSIsICIzMCUiKS4gVGhlc2UgdmFyeWluZyBzaXplcyBhcmUgc2VsZWN0ZWQgdG8gcHJvdmlkZSBpbnNpZ2h0IGludG8gdGhlIG1vZGVsJ3MgcGVyZm9ybWFuY2Ugd2l0aCBkaWZmZXJlbnQgYW1vdW50cyBvZiBkYXRhLCB3aGljaCBpcyB2aXRhbCBmb3IgZGV0ZWN0aW5nIHVuaXF1ZSBwYXR0ZXJucyBhbmQgY29uZmlybWluZyB0aGUgbW9kZWwncyBjb25zaXN0ZW5jeSBpbiB2YXJpb3VzIHNpdHVhdGlvbnMuDQoNCkluIHRoZSBmaW5hbCBzdGVwcywgd2UgdXRpbGl6ZSBkYXRhIHZpc3VhbGl6YXRpb24gdG9vbHMgdG8gY3JlYXRlIHZpc3VhbCByZXByZXNlbnRhdGlvbnMgb2Ygb3VyIGRlY2lzaW9uIHRyZWVzLiBBZGRpdGlvbmFsbHksIHdlIGNvbmR1Y3QgYW4gZXhoYXVzdGl2ZSBldmFsdWF0aW9uIG9mIHRoZSBtb2RlbCwgZW1wbG95aW5nIGEgIkNvbmZ1c2lvbiBNYXRyaXgiIHRvIGlsbHVzdHJhdGUgdGhlIG91dGNvbWVzIGNsZWFybHkuDQoNCg0KDQoNCioqSW5mb3JtYXRpb24gR2FpbioqDQoNCkluZm9ybWF0aW9uIEdhaW4gaXMgcGFydGljdWxhcmx5IHVzZWZ1bCB3aGVuIGRlYWxpbmcgd2l0aCBjYXRlZ29yaWNhbCB0YXJnZXQgdmFyaWFibGVzIHVzaW5nIEluZm9ybWF0aW9uIEdhaW4gZm9yIHRoZSBpbml0aWFsIHBhcnRpdGlvbmluZyBvZiBhIGRhdGFzZXQgd2hlbiBidWlsZGluZyBhIGRlY2lzaW9uIHRyZWUgSXQncyBiYXNlZCBvbiB0aGUgY29uY2VwdCBvZiBlbnRyb3B5IGFuZCBhaW1zIHRvIG1heGltaXplIHRoZSBob21vZ2VuZWl0eSBvZiBzdWJzZXRzIGFmdGVyIGVhY2ggc3BsaXQuIFRoaXMgYXBwcm9hY2ggaGVscHMgY3JlYXRlIGFuIGVmZmVjdGl2ZSBkZWNpc2lvbiB0cmVlIGJ5IHNlbGVjdGluZyBmZWF0dXJlcyB0aGF0IHByb3ZpZGUgdGhlIG1vc3QgaW5mb3JtYXRpb24gZm9yIHByZWRpY3RpbmcgdGhlIHRhcmdldCB2YXJpYWJsZS4NCg0KDQoqKjEtSW5mb3JtYXRpb24gR2Fpbig3MCUsMzAlKSoqDQoNCmBgYHtyfQ0KIyBMb2FkIHRoZSByZXF1aXJlZCBwYWNrYWdlcw0KbGlicmFyeShycGFydCkNCmxpYnJhcnkocnBhcnQucGxvdCkNCmxpYnJhcnkoY2FyZXQpDQoNCiMgU2V0IHRoZSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCnNldC5zZWVkKDEyMzQpDQoNCiMgU3BsaXQgdGhlIGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzDQppbmQgPC0gc2FtcGxlKDIsIG5yb3coSG9iYnlfRGF0YSksIHJlcGxhY2U9VFJVRSwgcHJvYj1jKDAuNywgMC4zKSkNCnRyYWluRGF0YSA8LSBIb2JieV9EYXRhW2luZCA9PSAxLF0NCnRlc3REYXRhIDwtIEhvYmJ5X0RhdGFbaW5kID09IDIsXQ0KDQojIERlZmluZSB0aGUgZm9ybXVsYSBmb3IgdGhlIGRlY2lzaW9uIHRyZWUNCm15Rm9ybXVsYSA8LSBgUHJlZGljdGVkIEhvYmJ5YCB+IFNjaG9sYXJzaGlwICsgRmF2X3N1YiArIFByb2plY3RzICsgR3Jhc3BfcG93ICsgVGltZV9zcHJ0ICsgQ2FyZWVyX3NwcnQgKyBBY3Rfc3BydCArIEZhbnRfYXJ0cyArIFdvbl9hcnRzICsgVGltZV9hcnQgKyBPbHltcGlhZF9QYXJ0aWNpcGF0aW9uDQoNCiMgQ3JlYXRlIHRoZSBkZWNpc2lvbiB0cmVlIG1vZGVsIHdpdGggdGhlICJpbmZvcm1hdGlvbiIgc3BsaXR0aW5nIGNyaXRlcmlvbg0KSG9iYnlfRGF0YV9jdHJlZSA8LSBycGFydChteUZvcm11bGEsIGRhdGEgPSB0cmFpbkRhdGEsIG1ldGhvZCA9ICJjbGFzcyIsIHBhcm1zID0gbGlzdChzcGxpdCA9ICJpbmZvcm1hdGlvbiIpKQ0KDQoNCg0KDQoNCiMgUHJpbnQgdGhlIGRlY2lzaW9uIHRyZWUNCnByaW50KEhvYmJ5X0RhdGFfY3RyZWUpDQoNCiMgUGxvdCB0aGUgZGVjaXNpb24gdHJlZQ0KDQpycGFydC5wbG90KEhvYmJ5X0RhdGFfY3RyZWUpDQoNCg0KDQoNCmBgYA0KDQoqKkRlY2lzaW9uIFRyZWUgQW5hbHlzaXMgVXNpbmcgSW5mb3JtYXRpb24gZ2Fpbig3MC8zMCk6KioNCg0KSW4gRnJpc3QgVHJlZSAsd2UgZGV2aWRlIGRhdGFzZXQgaW50byB0cmFpbmluZyBzZXQgYW5kIHRlc3Qgc2V0IHdpdGggc2l6ZSglNzAsJTMwKSByZXNwZWN0aXZlbHkuIEFzIHlvdSBjYW4gc2VlIGluIHRoZSBmaWd1cmUsIHRoZSByb290IG5vZGUgKCJDYXJlZXJfc3BydCIpIHNlcnZlcyBhcyB0aGUgc3RhcnRpbmcgcG9pbnQgZm9yIHRoZSBjbGFzc2lmaWNhdGlvbiBwcm9jZXNzIHNpbmNlIGhhdmUgdGhlIGhlaWdodHMgR2Fpbi4gVGhlIGRhdGFzZXQgaGFzIGEgZGlzdHJpYnV0aW9uIG9mIGFwcHJveGltYXRlbHkgNDIuNDclIGZvciBjbGFzcyAxKCJBY2FkZW1pY3MiKSwgMjUuOTIlIGZvciBjbGFzcyAyKCJBcnRzIiksIGFuZCAzMS42MSUgZm9yIGNsYXNzIDMgKCJTcG9ydHMiKS4NCg0KVGhlIHRyZWUgZnVydGhlciBicmFuY2hlcyBiYXNlZCBvbiB0aGUgdmFsdWVzIG9mIHRoZSAiQ2FyZWVyX3NwcnQiIGlmIGVxdWFsIDAsIHRyZWUgYnJhbmNoZXMgYmFzZWQgb24iV29uX2FydHMiICxhbmQgbWFqb3JpdHkgb2YgaW5zdGFuY2VzIGZhbGwgaW50byAiQWNhZGltY3MiLCBjb25zdGl0dXRpbmcgNjMuOTMlICwgaWYiV29uX2FydHMiIGVxdWFsIDAgb3IyIEluIHRoaXMgY2FzZSB0aGUgdHJlZSB0ZXJtaW5hdGVzIHdpdGggYSBsZWFmIG5vZGUgaW5kaWNhdGluZyBhIGhpZ2ggcHJvYmFiaWxpdHkgODkuMDglIGZvciAiQWNhZGltY3MiLmVsc2UgdGhlIGluc3RhbmNlcyBhcmUgY2xhc3NpZmllZCBiYXNlZCBvbiAiRmFudF9hcnRzIiBpbnRvICJBcnRzIiB3aXRoIGEgcHJvYmFiaWxpdHkgODglLGlmIGVxdWFsIDAgdGhlIGluc3RhbmNlcyBhcmUgY2xhc3NpZmllZCBpbnRvICJBY2FkZW1pY3MiIHdpdGggYSBwcm9iYWJpbGl0eSBvZiA3MyUgLGVsc2UgdGhlIGluc3RhbmNlcyBhcmUgY2xhc3NpZmllZCBpbnRvICJBcnRzIiB3aXRoIGEgaGlnaCBwcm9iYWJpbGl0eSA5NiUsIGlmICJDYXJlZXJfc3BydCIgaXMgMSwgdGhlIHRyZWUgZnVydGhlciBicmFuY2hlcyBiYXNlZCBvbiB0aGUgIkZhbnRfYXJ0cyIgaW50byAiU3BvcnRzIiB3aXRoIGEgcHJvYmFiaWxpdHkgODAlLiBJZiAiRmFudF9hcnRzIiBpcyAxICwgdGhlcmUgaXMgYW5vdGhlciBzcGxpdCBiYXNlZCBvbiB0aGUgIlRpbWVfYXJ0IiBmZWF0dXJlIGludG8gIkFydHMiIHdpdGggYSBwcm9iYWJpbGl0eSA0OSUuIElmICJUaW1lX2FydCIgaXMgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIDMsIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgaW50byAiQXJ0cyIgd2l0aCBhIHByb2JhYmlsaXR5IG9mIDg3JSAuIE9uIHRoZSBvdGhlciBoYW5kLCBpZiAiVGltZV9hcnQiIGlzIGxlc3MgdGhhbiAzLCB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGludG8gIlNwb3J0cyIgd2l0aCBhIHByb2JhYmlsaXR5IG9mIDY1JSAuSWYgIkZhbnRfYXJ0cyIgbm90IGVxdWFsIDEgLCB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGludG8gIlNwb3J0cyIgd2l0aCBhIGhpZ2ggcHJvYmFiaWxpdHkgOTUlLCBhcyBpbmRpY2F0ZWQgYnkgdGhlIGxlYWYgbm9kZS4NCg0KKipGaXJzdCBDb25mdXNpb24gbWF0cml4KioNCg0KYGBge3J9DQojIFByZWRpY3Qgb24gdGhlIHRlc3QgZGF0YQ0KdGVzdFByZWQgPC0gcHJlZGljdChIb2JieV9EYXRhX2N0cmVlLCBuZXdkYXRhID0gdGVzdERhdGEsIHR5cGUgPSAnY2xhc3MnKQ0KDQojIENoZWNrIHRoZSBhY2N1cmFjeSBvZiB0aGUgbW9kZWwNCmFjY3VyYWN5IDwtIHN1bSh0ZXN0UHJlZCA9PSB0ZXN0RGF0YSRgUHJlZGljdGVkIEhvYmJ5YCkgLyBucm93KHRlc3REYXRhKSAqIDEwMA0KY2F0KCdBY2N1cmFjeTonLCBhY2N1cmFjeSwgJ1xuJykNCg0KIyBDcmVhdGUgYSBjb25mdXNpb24gbWF0cml4DQpjb25mX21hdHJpeCA8LSB0YWJsZShBY3R1YWwgPSB0ZXN0RGF0YSRgUHJlZGljdGVkIEhvYmJ5YCwgUHJlZGljdGVkID0gdGVzdFByZWQpDQoNCiMgQ2FsY3VsYXRlIHByZWNpc2lvbiBmb3IgZWFjaCBjbGFzcw0KcHJlY2lzaW9uX2NsYXNzXzEgPC0gY29uZl9tYXRyaXhbMSwgMV0gLyBzdW0oY29uZl9tYXRyaXhbMSwgXSkNCnByZWNpc2lvbl9jbGFzc18yIDwtIGNvbmZfbWF0cml4WzIsIDJdIC8gc3VtKGNvbmZfbWF0cml4WzIsIF0pDQpwcmVjaXNpb25fY2xhc3NfMyA8LSBjb25mX21hdHJpeFszLCAzXSAvIHN1bShjb25mX21hdHJpeFszLCBdKQ0KDQojIENhbGN1bGF0ZSBzZW5zaXRpdml0eSBmb3IgZWFjaCBjbGFzcw0Kc2Vuc2l0aXZpdHlfY2xhc3NfMSA8LSBjb25mX21hdHJpeFsxLCAxXSAvIHN1bShjb25mX21hdHJpeFsxLCBdKQ0Kc2Vuc2l0aXZpdHlfY2xhc3NfMiA8LSBjb25mX21hdHJpeFsyLCAyXSAvIHN1bShjb25mX21hdHJpeFsyLCBdKQ0Kc2Vuc2l0aXZpdHlfY2xhc3NfMyA8LSBjb25mX21hdHJpeFszLCAzXSAvIHN1bShjb25mX21hdHJpeFszLCBdKQ0KDQojIENhbGN1bGF0ZSBzcGVjaWZpY2l0eSBmb3IgZWFjaCBjbGFzcw0Kc3BlY2lmaWNpdHlfY2xhc3NfMSA8LSBzdW0oZGlhZyhjb25mX21hdHJpeFstMSwgLTFdKSkgLyBzdW0oY29uZl9tYXRyaXhbLTEsIF0pDQpzcGVjaWZpY2l0eV9jbGFzc18yIDwtIHN1bShjb25mX21hdHJpeFtjKDEsIDMpLCBjKDEsIDMpXSkgLyBzdW0oY29uZl9tYXRyaXhbYygxLCAzKSwgXSkNCnNwZWNpZmljaXR5X2NsYXNzXzMgPC0gc3VtKGNvbmZfbWF0cml4W2MoMSwgMiksIGMoMSwgMildKSAvIHN1bShjb25mX21hdHJpeFtjKDEsIDIpLCBdKQ0KDQojIENhbGN1bGF0ZSBtYWNyby1hdmVyYWdlIHNlbnNpdGl2aXR5DQptYWNyb19hdmdfc2Vuc2l0aXZpdHkgPC0gKHNlbnNpdGl2aXR5X2NsYXNzXzEgKyBzZW5zaXRpdml0eV9jbGFzc18yICsgc2Vuc2l0aXZpdHlfY2xhc3NfMykgLyAzDQoNCiMgQ2FsY3VsYXRlIG1hY3JvLWF2ZXJhZ2Ugc3BlY2lmaWNpdHkNCm1hY3JvX2F2Z19zcGVjaWZpY2l0eSA8LSAoc3BlY2lmaWNpdHlfY2xhc3NfMSArIHNwZWNpZmljaXR5X2NsYXNzXzIgKyBzcGVjaWZpY2l0eV9jbGFzc18zKSAvIDMNCg0KIyBDYWxjdWxhdGUgbWFjcm8tYXZlcmFnZSBwcmVjaXNpb24NCm1hY3JvX2F2Z19wcmVjaXNpb24gPC0gKHByZWNpc2lvbl9jbGFzc18xICsgcHJlY2lzaW9uX2NsYXNzXzIgKyBwcmVjaXNpb25fY2xhc3NfMykgLyAzDQoNCiMgUHJpbnQgbWFjcm8tYXZlcmFnZSBzZW5zaXRpdml0eQ0KY2F0KCdBdmVyYWdlIFNlbnNpdGl2aXR5OicsIG1hY3JvX2F2Z19zZW5zaXRpdml0eSwgJ1xuJykNCg0KIyBQcmludCBtYWNyby1hdmVyYWdlIHNwZWNpZmljaXR5DQpjYXQoJ0F2ZXJhZ2UgU3BlY2lmaWNpdHk6JywgbWFjcm9fYXZnX3NwZWNpZmljaXR5LCAnXG4nKQ0KDQojIFByaW50IG1hY3JvLWF2ZXJhZ2UgcHJlY2lzaW9uDQpjYXQoJ0F2ZXJhZ2UgUHJlY2lzaW9uOicsIG1hY3JvX2F2Z19wcmVjaXNpb24sICdcbicpDQoNCg0KDQoNCg0KDQojIFByaW50IHByZWNpc2lvbiBmb3IgZWFjaCBjbGFzcw0KY2F0KCdQcmVjaXNpb24gZm9yIENsYXNzIDE6JywgcHJlY2lzaW9uX2NsYXNzXzEsICcgXG4nKQ0KDQpjYXQoJ1ByZWNpc2lvbiBmb3IgQ2xhc3MgMjonLCBwcmVjaXNpb25fY2xhc3NfMiwgJyAgXG4nKQ0KY2F0KCdQcmVjaXNpb24gZm9yIENsYXNzIDM6JywgcHJlY2lzaW9uX2NsYXNzXzMsICcgXG4nKQ0KDQojIFByaW50IHNlbnNpdGl2aXR5IGZvciBlYWNoIGNsYXNzDQpjYXQoJ1NlbnNpdGl2aXR5IGZvciBDbGFzcyAxOicsIHNlbnNpdGl2aXR5X2NsYXNzXzEsICdcbicpDQpjYXQoJ1NlbnNpdGl2aXR5IGZvciBDbGFzcyAyOicsIHNlbnNpdGl2aXR5X2NsYXNzXzIsICdcbicpDQpjYXQoJ1NlbnNpdGl2aXR5IGZvciBDbGFzcyAzOicsIHNlbnNpdGl2aXR5X2NsYXNzXzMsICdcbicpDQoNCiMgUHJpbnQgc3BlY2lmaWNpdHkgZm9yIGVhY2ggY2xhc3MNCmNhdCgnU3BlY2lmaWNpdHkgZm9yIENsYXNzIDE6Jywgc3BlY2lmaWNpdHlfY2xhc3NfMSwgJ1xuJykNCmNhdCgnU3BlY2lmaWNpdHkgZm9yIENsYXNzIDI6Jywgc3BlY2lmaWNpdHlfY2xhc3NfMiwgJ1xuJykNCmNhdCgnU3BlY2lmaWNpdHkgZm9yIENsYXNzIDM6Jywgc3BlY2lmaWNpdHlfY2xhc3NfMywgJ1xuJykNCg0KDQoNCg0KYGBgDQoNCg0KDQoqKjItSW5mb3JtYXRpb24gR2Fpbig4MCUsMjAlKSoqDQoNCmBgYHtyfQ0KbGlicmFyeShycGFydCkNCg0KbGlicmFyeShjYVRvb2xzKQ0KbGlicmFyeShycGFydC5wbG90KQ0KbGlicmFyeShjYXJldCkNCg0KIyBTZXQgdGhlIHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQ0Kc2V0LnNlZWQoMTIzNCkNCg0KIyBTcGxpdCB0aGUgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldHMNCmluZCA8LSBzYW1wbGUoMiwgbnJvdyhIb2JieV9EYXRhKSwgcmVwbGFjZT1UUlVFLCBwcm9iPWMoMC44LCAwLjIpKQ0KdHJhaW5EYXRhIDwtIEhvYmJ5X0RhdGFbaW5kID09IDEsXQ0KdGVzdERhdGEgPC0gSG9iYnlfRGF0YVtpbmQgPT0gMixdDQoNCiMgRGVmaW5lIHRoZSBmb3JtdWxhIGZvciB0aGUgZGVjaXNpb24gdHJlZQ0KbXlGb3JtdWxhIDwtIGBQcmVkaWN0ZWQgSG9iYnlgIH4gU2Nob2xhcnNoaXAgKyBGYXZfc3ViICsgUHJvamVjdHMgKyBHcmFzcF9wb3cgKyBUaW1lX3NwcnQgKyBDYXJlZXJfc3BydCArIEFjdF9zcHJ0ICsgRmFudF9hcnRzICsgV29uX2FydHMgKyBUaW1lX2FydCArIE9seW1waWFkX1BhcnRpY2lwYXRpb24NCg0KIyBDcmVhdGUgdGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwgd2l0aCB0aGUgImluZm9ybWF0aW9uIiBzcGxpdHRpbmcgY3JpdGVyaW9uDQpIb2JieV9EYXRhX2N0cmVlIDwtIHJwYXJ0KG15Rm9ybXVsYSwgZGF0YSA9IHRyYWluRGF0YSwgbWV0aG9kID0gImNsYXNzIiwgcGFybXMgPSBsaXN0KHNwbGl0ID0gImluZm9ybWF0aW9uIikpDQoNCg0KDQoNCg0KIyBQcmludCB0aGUgZGVjaXNpb24gdHJlZQ0KcHJpbnQoSG9iYnlfRGF0YV9jdHJlZSkNCg0KIyBQbG90IHRoZSBkZWNpc2lvbiB0cmVlDQoNCnJwYXJ0LnBsb3QoSG9iYnlfRGF0YV9jdHJlZSkNCg0KDQoNCg0KDQoNCmBgYA0KDQoqKkRlY2lzaW9uIFRyZWUgQW5hbHlzaXMgVXNpbmcgSW5mb3JtYXRpb24gZ2Fpbig4MC8yMCk6KioNCg0KSW4gU2Vjb25kIFRyZWUgLHdlIGRldmlkZSBkYXRhc2V0IGludG8gdHJhaW5pbmcgc2V0IGFuZCB0ZXN0IHNldCB3aXRoIHNpemUoJTgwLCUyMCkgcmVzcGVjdGl2ZWx5LiBBcyB5b3UgY2FuIHNlZSBpbiB0aGUgZmlndXJlLCB0aGUgcm9vdCBub2RlICgiQ2FyZWVyX3NwcnQiKSBzZXJ2ZXMgYXMgdGhlIHN0YXJ0aW5nIHBvaW50IGZvciB0aGUgY2xhc3NpZmljYXRpb24gcHJvY2VzcyBzaW5jZSBoYXZlIHRoZSBoZWlnaHRzIEdhaW4uIFRoZSBkYXRhc2V0IGhhcyBhIGRpc3RyaWJ1dGlvbiBvZiBhcHByb3hpbWF0ZWx5IDQzJWZvciBjbGFzczEgKCJBY2FkZW1pY3MiKSwgMjYlIGZvciBjbGFzcyAyKCJBcnRzIiksIGFuZCAzMSUgZm9yIGNsYXNzIDMgKCJTcG9ydHMiKS4NCg0KVGhlIHRyZWUgZnVydGhlciBicmFuY2hlcyBiYXNlZCBvbiB0aGUgdmFsdWVzIG9mIHRoZSAiQ2FyZWVyX3NwcnQiIGlmIGVxdWFsIDAsIHRyZWUgYnJhbmNoZXMgYmFzZWQgb24iV29uX2FydHMiICxhbmQgbWFqb3JpdHkgb2YgaW5zdGFuY2VzIGZhbGwgaW50byAiQWNhZGltY3MiLCBjb25zdGl0dXRpbmcgNjUlICwgaWYiV29uX2FydHMiIGVxdWFsIDAgb3IyIEluIHRoaXMgY2FzZSB0aGUgdHJlZSB0ZXJtaW5hdGVzIHdpdGggYSBsZWFmIG5vZGUgaW5kaWNhdGluZyBhIGhpZ2ggcHJvYmFiaWxpdHkgOTAlIGZvciAiQWNhZGltY3MiLmVsc2UgdGhlIGluc3RhbmNlcyBhcmUgY2xhc3NpZmllZCBiYXNlZCBvbiAiRmFudF9hcnRzImludG8gIkFydHMiIHdpdGggYSBwcm9iYWJpbGl0eSA4OCUsaWYgZXF1YWwgMCB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGludG8gIkFjYWRlbWljcyIgd2l0aCBhIHByb2JhYmlsaXR5IG9mIDcyJSAsZWxzZSB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGludG8gIkFydHMiIHdpdGggYSBoaWdoIHByb2JhYmlsaXR5IDk2JSwgaWYgIkNhcmVlcl9zcHJ0IiBpcyAxLCB0aGUgdHJlZSBmdXJ0aGVyIGJyYW5jaGVzIGJhc2VkIG9uIHRoZSAiRmFudF9hcnRzIiBpbnRvICJTcG9ydHMiIHdpdGggYSBwcm9iYWJpbGl0eSA3OCUuIElmICJGYW50X2FydHMiIGlzIDEgLCB0aGVyZSBpcyBhbm90aGVyIHNwbGl0IGJhc2VkIG9uIHRoZSAiVGltZV9hcnQiIGludG8gIkFydHMiIHdpdGggYSBwcm9iYWJpbGl0eSA1MCUuIElmICJUaW1lX2FydCIgaXMgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIDMsIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgaW50byAiQXJ0cyIgd2l0aCBhIHByb2JhYmlsaXR5IG9mIDg1JSAuIE9uIHRoZSBvdGhlciBoYW5kLCBpZiAiVGltZV9hcnQiIGlzIGxlc3MgdGhhbiAzLCB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGludG8gIlNwb3J0cyIgd2l0aCBhIHByb2JhYmlsaXR5IG9mIDU4JSAuSWYgIkZhbnRfYXJ0cyIgSWYgbm90IGVxdWFsIDEgLCB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGludG8gIlNwb3J0cyIgd2l0aCBhIGhpZ2ggcHJvYmFiaWxpdHkgOTQlLCBhcyBpbmRpY2F0ZWQgYnkgdGhlIGxlYWYgbm9kZS4NCg0KKipTZWNvbmQgQ29uZnVzaW9uIG1hdHJpeCoqDQoNCmBgYHtyfQ0KDQojIFByZWRpY3Qgb24gdGhlIHRlc3QgZGF0YQ0KdGVzdFByZWQgPC0gcHJlZGljdChIb2JieV9EYXRhX2N0cmVlLCBuZXdkYXRhID0gdGVzdERhdGEsIHR5cGUgPSAnY2xhc3MnKQ0KDQojIENoZWNrIHRoZSBhY2N1cmFjeSBvZiB0aGUgbW9kZWwNCmFjY3VyYWN5IDwtIHN1bSh0ZXN0UHJlZCA9PSB0ZXN0RGF0YSRgUHJlZGljdGVkIEhvYmJ5YCkgLyBucm93KHRlc3REYXRhKSAqIDEwMA0KY2F0KCdBY2N1cmFjeTonLCBhY2N1cmFjeSwgJ1xuJykNCg0KIyBDcmVhdGUgYSBjb25mdXNpb24gbWF0cml4DQpjb25mX21hdHJpeCA8LSB0YWJsZShBY3R1YWwgPSB0ZXN0RGF0YSRgUHJlZGljdGVkIEhvYmJ5YCwgUHJlZGljdGVkID0gdGVzdFByZWQpDQoNCiMgQ2FsY3VsYXRlIHByZWNpc2lvbiBmb3IgZWFjaCBjbGFzcw0KcHJlY2lzaW9uX2NsYXNzXzEgPC0gY29uZl9tYXRyaXhbMSwgMV0gLyBzdW0oY29uZl9tYXRyaXhbMSwgXSkNCnByZWNpc2lvbl9jbGFzc18yIDwtIGNvbmZfbWF0cml4WzIsIDJdIC8gc3VtKGNvbmZfbWF0cml4WzIsIF0pDQpwcmVjaXNpb25fY2xhc3NfMyA8LSBjb25mX21hdHJpeFszLCAzXSAvIHN1bShjb25mX21hdHJpeFszLCBdKQ0KDQojIENhbGN1bGF0ZSBzZW5zaXRpdml0eSBmb3IgZWFjaCBjbGFzcw0Kc2Vuc2l0aXZpdHlfY2xhc3NfMSA8LSBjb25mX21hdHJpeFsxLCAxXSAvIHN1bShjb25mX21hdHJpeFsxLCBdKQ0Kc2Vuc2l0aXZpdHlfY2xhc3NfMiA8LSBjb25mX21hdHJpeFsyLCAyXSAvIHN1bShjb25mX21hdHJpeFsyLCBdKQ0Kc2Vuc2l0aXZpdHlfY2xhc3NfMyA8LSBjb25mX21hdHJpeFszLCAzXSAvIHN1bShjb25mX21hdHJpeFszLCBdKQ0KDQojIENhbGN1bGF0ZSBzcGVjaWZpY2l0eSBmb3IgZWFjaCBjbGFzcw0Kc3BlY2lmaWNpdHlfY2xhc3NfMSA8LSBzdW0oZGlhZyhjb25mX21hdHJpeFstMSwgLTFdKSkgLyBzdW0oY29uZl9tYXRyaXhbLTEsIF0pDQpzcGVjaWZpY2l0eV9jbGFzc18yIDwtIHN1bShjb25mX21hdHJpeFtjKDEsIDMpLCBjKDEsIDMpXSkgLyBzdW0oY29uZl9tYXRyaXhbYygxLCAzKSwgXSkNCnNwZWNpZmljaXR5X2NsYXNzXzMgPC0gc3VtKGNvbmZfbWF0cml4W2MoMSwgMiksIGMoMSwgMildKSAvIHN1bShjb25mX21hdHJpeFtjKDEsIDIpLCBdKQ0KDQojIENhbGN1bGF0ZSBtYWNyby1hdmVyYWdlIHNlbnNpdGl2aXR5DQptYWNyb19hdmdfc2Vuc2l0aXZpdHkgPC0gKHNlbnNpdGl2aXR5X2NsYXNzXzEgKyBzZW5zaXRpdml0eV9jbGFzc18yICsgc2Vuc2l0aXZpdHlfY2xhc3NfMykgLyAzDQoNCiMgQ2FsY3VsYXRlIG1hY3JvLWF2ZXJhZ2Ugc3BlY2lmaWNpdHkNCm1hY3JvX2F2Z19zcGVjaWZpY2l0eSA8LSAoc3BlY2lmaWNpdHlfY2xhc3NfMSArIHNwZWNpZmljaXR5X2NsYXNzXzIgKyBzcGVjaWZpY2l0eV9jbGFzc18zKSAvIDMNCg0KIyBDYWxjdWxhdGUgbWFjcm8tYXZlcmFnZSBwcmVjaXNpb24NCm1hY3JvX2F2Z19wcmVjaXNpb24gPC0gKHByZWNpc2lvbl9jbGFzc18xICsgcHJlY2lzaW9uX2NsYXNzXzIgKyBwcmVjaXNpb25fY2xhc3NfMykgLyAzDQoNCiMgUHJpbnQgbWFjcm8tYXZlcmFnZSBzZW5zaXRpdml0eQ0KY2F0KCdBdmVyYWdlIFNlbnNpdGl2aXR5OicsIG1hY3JvX2F2Z19zZW5zaXRpdml0eSwgJ1xuJykNCg0KIyBQcmludCBtYWNyby1hdmVyYWdlIHNwZWNpZmljaXR5DQpjYXQoJ0F2ZXJhZ2UgU3BlY2lmaWNpdHk6JywgbWFjcm9fYXZnX3NwZWNpZmljaXR5LCAnXG4nKQ0KDQojIFByaW50IG1hY3JvLWF2ZXJhZ2UgcHJlY2lzaW9uDQpjYXQoJ0F2ZXJhZ2UgUHJlY2lzaW9uOicsIG1hY3JvX2F2Z19wcmVjaXNpb24sICdcbicpDQoNCg0KDQoNCg0KDQojIFByaW50IHByZWNpc2lvbiBmb3IgZWFjaCBjbGFzcw0KY2F0KCdQcmVjaXNpb24gZm9yIENsYXNzIDE6JywgcHJlY2lzaW9uX2NsYXNzXzEsICcgXG4nKQ0KDQpjYXQoJ1ByZWNpc2lvbiBmb3IgQ2xhc3MgMjonLCBwcmVjaXNpb25fY2xhc3NfMiwgJyAgXG4nKQ0KY2F0KCdQcmVjaXNpb24gZm9yIENsYXNzIDM6JywgcHJlY2lzaW9uX2NsYXNzXzMsICcgXG4nKQ0KDQojIFByaW50IHNlbnNpdGl2aXR5IGZvciBlYWNoIGNsYXNzDQpjYXQoJ1NlbnNpdGl2aXR5IGZvciBDbGFzcyAxOicsIHNlbnNpdGl2aXR5X2NsYXNzXzEsICdcbicpDQpjYXQoJ1NlbnNpdGl2aXR5IGZvciBDbGFzcyAyOicsIHNlbnNpdGl2aXR5X2NsYXNzXzIsICdcbicpDQpjYXQoJ1NlbnNpdGl2aXR5IGZvciBDbGFzcyAzOicsIHNlbnNpdGl2aXR5X2NsYXNzXzMsICdcbicpDQoNCiMgUHJpbnQgc3BlY2lmaWNpdHkgZm9yIGVhY2ggY2xhc3MNCmNhdCgnU3BlY2lmaWNpdHkgZm9yIENsYXNzIDE6Jywgc3BlY2lmaWNpdHlfY2xhc3NfMSwgJ1xuJykNCmNhdCgnU3BlY2lmaWNpdHkgZm9yIENsYXNzIDI6Jywgc3BlY2lmaWNpdHlfY2xhc3NfMiwgJ1xuJykNCmNhdCgnU3BlY2lmaWNpdHkgZm9yIENsYXNzIDM6Jywgc3BlY2lmaWNpdHlfY2xhc3NfMywgJ1xuJykNCg0KDQoNCg0KDQoNCg0KYGBgDQoNCioqMy1JbmZvcm1hdGlvbiBHYWluKDkwJSwxMCUpKioNCg0KYGBge3J9DQpsaWJyYXJ5KHJwYXJ0KQ0KDQpsaWJyYXJ5KGNhVG9vbHMpDQpsaWJyYXJ5KHJwYXJ0LnBsb3QpDQpsaWJyYXJ5KGNhcmV0KQ0KIyBTZXQgdGhlIHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQ0Kc2V0LnNlZWQoMTIzNCkNCg0KIyBTcGxpdCB0aGUgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldHMNCmluZCA8LSBzYW1wbGUoMiwgbnJvdyhIb2JieV9EYXRhKSwgcmVwbGFjZT1UUlVFLCBwcm9iPWMoMC45LCAwLjEpKQ0KdHJhaW5EYXRhIDwtIEhvYmJ5X0RhdGFbaW5kID09IDEsXQ0KdGVzdERhdGEgPC0gSG9iYnlfRGF0YVtpbmQgPT0gMixdDQoNCiMgRGVmaW5lIHRoZSBmb3JtdWxhIGZvciB0aGUgZGVjaXNpb24gdHJlZQ0KbXlGb3JtdWxhIDwtIGBQcmVkaWN0ZWQgSG9iYnlgIH4gU2Nob2xhcnNoaXAgKyBGYXZfc3ViICsgUHJvamVjdHMgKyBHcmFzcF9wb3cgKyBUaW1lX3NwcnQgKyBDYXJlZXJfc3BydCArIEFjdF9zcHJ0ICsgRmFudF9hcnRzICsgV29uX2FydHMgKyBUaW1lX2FydCArIE9seW1waWFkX1BhcnRpY2lwYXRpb24NCg0KIyBDcmVhdGUgdGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwgd2l0aCB0aGUgImluZm9ybWF0aW9uIiBzcGxpdHRpbmcgY3JpdGVyaW9uDQpIb2JieV9EYXRhX2N0cmVlIDwtIHJwYXJ0KG15Rm9ybXVsYSwgZGF0YSA9IHRyYWluRGF0YSwgbWV0aG9kID0gImNsYXNzIiwgcGFybXMgPSBsaXN0KHNwbGl0ID0gImluZm9ybWF0aW9uIikpDQoNCg0KDQojIFByaW50IHRoZSBkZWNpc2lvbiB0cmVlDQpwcmludChIb2JieV9EYXRhX2N0cmVlKQ0KDQojIFBsb3QgdGhlIGRlY2lzaW9uIHRyZWUNCg0KcnBhcnQucGxvdChIb2JieV9EYXRhX2N0cmVlKQ0KDQoNCg0KDQpgYGANCg0KKipEZWNpc2lvbiBUcmVlIEFuYWx5c2lzIFVzaW5nIEluZm9ybWF0aW9uIGdhaW4oOTAvMTApOioqDQoNCkluIFRoaXJkIFRyZWUgLHdlIGRldmlkZSBkYXRhc2V0IGludG8gdHJhaW5pbmcgc2V0IGFuZCB0ZXN0IHNldCB3aXRoIHNpemUoJTkwLCUxMCkgcmVzcGVjdGl2ZWx5LiBBcyB5b3UgY2FuIHNlZSBpbiB0aGUgZmlndXJlLCB0aGUgcm9vdCBub2RlICgiQ2FyZWVyX3NwcnQiKSBzZXJ2ZXMgYXMgdGhlIHN0YXJ0aW5nIHBvaW50IGZvciB0aGUgY2xhc3NpZmljYXRpb24gcHJvY2VzcyBzaW5jZSBoYXZlIHRoZSBoZWlnaHRzIEdhaW4uIFRoZSBkYXRhc2V0IGhhcyBhIGRpc3RyaWJ1dGlvbiBvZiBhcHByb3hpbWF0ZWx5IDQzJSBmb3IgY2xhc3MgMSgiQWNhZGVtaWNzIiksIDI1JSBmb3IgY2xhc3MgMigiQXJ0cyIpLCBhbmQgMzIlIGZvciBjbGFzcyAzICgiU3BvcnRzIikuDQoNClRoZSB0cmVlIGZ1cnRoZXIgYnJhbmNoZXMgYmFzZWQgb24gdGhlIHZhbHVlcyBvZiB0aGUgIkNhcmVlcl9zcHJ0IiBpZiBlcXVhbCAwLCB0cmVlIGJyYW5jaGVzIGJhc2VkIG9uIldvbl9hcnRzIiAsYW5kIG1ham9yaXR5IG9mIGluc3RhbmNlcyBmYWxsIGludG8gIkFjYWRpbWNzIiwgY29uc3RpdHV0aW5nIDY1JSAsIGlmIldvbl9hcnRzIiBlcXVhbCAwIG9yIDIgSW4gdGhpcyBjYXNlIHRoZSB0cmVlIHRlcm1pbmF0ZXMgd2l0aCBhIGxlYWYgbm9kZSBpbmRpY2F0aW5nIGEgaGlnaCBwcm9iYWJpbGl0eSA5MCUgZm9yICJBY2FkaW1jcyIuZWxzZSB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGJhc2VkIG9uICJGYW50X2FydHMiaW50byAiQXJ0cyIgd2l0aCBhIHByb2JhYmlsaXR5IDg3JSxpZiBlcXVhbCAwIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgaW50byAiQWNhZGVtaWNzIiB3aXRoIGEgcHJvYmFiaWxpdHkgb2YgNzQlICxlbHNlIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgaW50byAiQXJ0cyIgd2l0aCBhIGhpZ2ggcHJvYmFiaWxpdHkgOTYlLCBpZiAiQ2FyZWVyX3NwcnQiIGlzIDEsIHRoZSB0cmVlIGZ1cnRoZXIgYnJhbmNoZXMgYmFzZWQgb24gdGhlICJGYW50X2FydHMiIGludG8gIlNwb3J0cyIgd2l0aCBhIHByb2JhYmlsaXR5IDc5JS4gSWYgIkZhbnRfYXJ0cyIgaXMgMSAsIHRoZXJlIGlzIGFub3RoZXIgc3BsaXQgYmFzZWQgb24gdGhlICJUaW1lX2FydCIgaW50byAiQXJ0cyIgd2l0aCBhIHByb2JhYmlsaXR5IDQ4JS4gSWYgIlRpbWVfYXJ0IiBpcyBncmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8gMywgdGhlIGluc3RhbmNlcyBhcmUgY2xhc3NpZmllZCBpbnRvICJBcnRzIiB3aXRoIGEgcHJvYmFiaWxpdHkgb2YgODQlIC4gT24gdGhlIG90aGVyIGhhbmQsIGlmICJUaW1lX2FydCIgaXMgbGVzcyB0aGFuIDMsIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgYmFzZWQgb24iQWN0X3NwcnQgIiBpbnRvICJTcG9ydHMiIHdpdGggYSBwcm9iYWJpbGl0eSBvZiA1OCUgLmlmICJBY3Rfc3BydCIgZXF1YWwgMCB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGludG8gIkFjYWRlbWljcyIgd2l0aCBhIHByb2JhYmlsaXR5IG9mIDU5JSwgb3RoZXIgaGFuZCB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGludG8gIlNwb3J0cyIgd2l0aCBhIHByb2JhYmlsaXR5IG9mIDc4JS4gSWYgIkZhbnRfYXJ0cyIgbm90IGVxdWFsIDEgLCB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGludG8gIlNwb3J0cyIgd2l0aCBhIGhpZ2ggcHJvYmFiaWxpdHkgOTQlLCBhcyBpbmRpY2F0ZWQgYnkgdGhlIGxlYWYgbm9kZS4NCg0KKipUaGlyZCBDb25mdXNpb24gbWF0cml4KioNCg0KYGBge3J9DQoNCiMgUHJlZGljdCBvbiB0aGUgdGVzdCBkYXRhDQp0ZXN0UHJlZCA8LSBwcmVkaWN0KEhvYmJ5X0RhdGFfY3RyZWUsIG5ld2RhdGEgPSB0ZXN0RGF0YSwgdHlwZSA9ICdjbGFzcycpDQoNCiMgQ2hlY2sgdGhlIGFjY3VyYWN5IG9mIHRoZSBtb2RlbA0KYWNjdXJhY3kgPC0gc3VtKHRlc3RQcmVkID09IHRlc3REYXRhJGBQcmVkaWN0ZWQgSG9iYnlgKSAvIG5yb3codGVzdERhdGEpICogMTAwDQpjYXQoJ0FjY3VyYWN5OicsIGFjY3VyYWN5LCAnXG4nKQ0KDQojIENyZWF0ZSBhIGNvbmZ1c2lvbiBtYXRyaXgNCmNvbmZfbWF0cml4IDwtIHRhYmxlKEFjdHVhbCA9IHRlc3REYXRhJGBQcmVkaWN0ZWQgSG9iYnlgLCBQcmVkaWN0ZWQgPSB0ZXN0UHJlZCkNCg0KIyBDYWxjdWxhdGUgcHJlY2lzaW9uIGZvciBlYWNoIGNsYXNzDQpwcmVjaXNpb25fY2xhc3NfMSA8LSBjb25mX21hdHJpeFsxLCAxXSAvIHN1bShjb25mX21hdHJpeFsxLCBdKQ0KcHJlY2lzaW9uX2NsYXNzXzIgPC0gY29uZl9tYXRyaXhbMiwgMl0gLyBzdW0oY29uZl9tYXRyaXhbMiwgXSkNCnByZWNpc2lvbl9jbGFzc18zIDwtIGNvbmZfbWF0cml4WzMsIDNdIC8gc3VtKGNvbmZfbWF0cml4WzMsIF0pDQoNCiMgQ2FsY3VsYXRlIHNlbnNpdGl2aXR5IGZvciBlYWNoIGNsYXNzDQpzZW5zaXRpdml0eV9jbGFzc18xIDwtIGNvbmZfbWF0cml4WzEsIDFdIC8gc3VtKGNvbmZfbWF0cml4WzEsIF0pDQpzZW5zaXRpdml0eV9jbGFzc18yIDwtIGNvbmZfbWF0cml4WzIsIDJdIC8gc3VtKGNvbmZfbWF0cml4WzIsIF0pDQpzZW5zaXRpdml0eV9jbGFzc18zIDwtIGNvbmZfbWF0cml4WzMsIDNdIC8gc3VtKGNvbmZfbWF0cml4WzMsIF0pDQoNCiMgQ2FsY3VsYXRlIHNwZWNpZmljaXR5IGZvciBlYWNoIGNsYXNzDQpzcGVjaWZpY2l0eV9jbGFzc18xIDwtIHN1bShkaWFnKGNvbmZfbWF0cml4Wy0xLCAtMV0pKSAvIHN1bShjb25mX21hdHJpeFstMSwgXSkNCnNwZWNpZmljaXR5X2NsYXNzXzIgPC0gc3VtKGNvbmZfbWF0cml4W2MoMSwgMyksIGMoMSwgMyldKSAvIHN1bShjb25mX21hdHJpeFtjKDEsIDMpLCBdKQ0Kc3BlY2lmaWNpdHlfY2xhc3NfMyA8LSBzdW0oY29uZl9tYXRyaXhbYygxLCAyKSwgYygxLCAyKV0pIC8gc3VtKGNvbmZfbWF0cml4W2MoMSwgMiksIF0pDQoNCiMgQ2FsY3VsYXRlIG1hY3JvLWF2ZXJhZ2Ugc2Vuc2l0aXZpdHkNCm1hY3JvX2F2Z19zZW5zaXRpdml0eSA8LSAoc2Vuc2l0aXZpdHlfY2xhc3NfMSArIHNlbnNpdGl2aXR5X2NsYXNzXzIgKyBzZW5zaXRpdml0eV9jbGFzc18zKSAvIDMNCg0KIyBDYWxjdWxhdGUgbWFjcm8tYXZlcmFnZSBzcGVjaWZpY2l0eQ0KbWFjcm9fYXZnX3NwZWNpZmljaXR5IDwtIChzcGVjaWZpY2l0eV9jbGFzc18xICsgc3BlY2lmaWNpdHlfY2xhc3NfMiArIHNwZWNpZmljaXR5X2NsYXNzXzMpIC8gMw0KDQojIENhbGN1bGF0ZSBtYWNyby1hdmVyYWdlIHByZWNpc2lvbg0KbWFjcm9fYXZnX3ByZWNpc2lvbiA8LSAocHJlY2lzaW9uX2NsYXNzXzEgKyBwcmVjaXNpb25fY2xhc3NfMiArIHByZWNpc2lvbl9jbGFzc18zKSAvIDMNCg0KIyBQcmludCBtYWNyby1hdmVyYWdlIHNlbnNpdGl2aXR5DQpjYXQoJ0F2ZXJhZ2UgU2Vuc2l0aXZpdHk6JywgbWFjcm9fYXZnX3NlbnNpdGl2aXR5LCAnXG4nKQ0KDQojIFByaW50IG1hY3JvLWF2ZXJhZ2Ugc3BlY2lmaWNpdHkNCmNhdCgnQXZlcmFnZSBTcGVjaWZpY2l0eTonLCBtYWNyb19hdmdfc3BlY2lmaWNpdHksICdcbicpDQoNCiMgUHJpbnQgbWFjcm8tYXZlcmFnZSBwcmVjaXNpb24NCmNhdCgnQXZlcmFnZSBQcmVjaXNpb246JywgbWFjcm9fYXZnX3ByZWNpc2lvbiwgJ1xuJykNCg0KDQoNCg0KDQoNCiMgUHJpbnQgcHJlY2lzaW9uIGZvciBlYWNoIGNsYXNzDQpjYXQoJ1ByZWNpc2lvbiBmb3IgQ2xhc3MgMTonLCBwcmVjaXNpb25fY2xhc3NfMSwgJyBcbicpDQoNCmNhdCgnUHJlY2lzaW9uIGZvciBDbGFzcyAyOicsIHByZWNpc2lvbl9jbGFzc18yLCAnICBcbicpDQpjYXQoJ1ByZWNpc2lvbiBmb3IgQ2xhc3MgMzonLCBwcmVjaXNpb25fY2xhc3NfMywgJyBcbicpDQoNCiMgUHJpbnQgc2Vuc2l0aXZpdHkgZm9yIGVhY2ggY2xhc3MNCmNhdCgnU2Vuc2l0aXZpdHkgZm9yIENsYXNzIDE6Jywgc2Vuc2l0aXZpdHlfY2xhc3NfMSwgJ1xuJykNCmNhdCgnU2Vuc2l0aXZpdHkgZm9yIENsYXNzIDI6Jywgc2Vuc2l0aXZpdHlfY2xhc3NfMiwgJ1xuJykNCmNhdCgnU2Vuc2l0aXZpdHkgZm9yIENsYXNzIDM6Jywgc2Vuc2l0aXZpdHlfY2xhc3NfMywgJ1xuJykNCg0KIyBQcmludCBzcGVjaWZpY2l0eSBmb3IgZWFjaCBjbGFzcw0KY2F0KCdTcGVjaWZpY2l0eSBmb3IgQ2xhc3MgMTonLCBzcGVjaWZpY2l0eV9jbGFzc18xLCAnXG4nKQ0KY2F0KCdTcGVjaWZpY2l0eSBmb3IgQ2xhc3MgMjonLCBzcGVjaWZpY2l0eV9jbGFzc18yLCAnXG4nKQ0KY2F0KCdTcGVjaWZpY2l0eSBmb3IgQ2xhc3MgMzonLCBzcGVjaWZpY2l0eV9jbGFzc18zLCAnXG4nKQ0KDQoNCmBgYA0KDQoqKkNvbXBhcmluZyBEZWNpc2lvbiBUcmVlIFJlc3VsdHMgVXNpbmcgSW5mcm9tYXRpb24gZ2FpbjoqKg0KQWZ0ZXIgdHJhaW5pbmcgdGhyZWUgdHJlZXMgd2l0aCBkaWZmZXJlbnQgc2l6ZXMsIGVtcGxveWluZyBpbmZvcm1hdGlvbiBnYWluIGFzIHRoZSBzZWxlY3Rpb24gbWVhc3VyZSwgb3VyIGFuYWx5c2lzIGxlZCB0byBjb25zaXN0ZW50IGFjY3VyYWN5IHJlc3VsdHMgYW1vbmcgdGhlIHRyZWVzOiBUcmVlIDEgKDAuODkzMiksIFRyZWUgMiAoMC45MDU3KSwgYW5kIFRyZWUgMyAoMC44NzIxKS4gVGhlIG1pbm9yIGRpc2NyZXBhbmNpZXMgb2JzZXJ2ZWQgaW4gdGhlc2UgYWNjdXJhY3kgdmFsdWVzIGNvdWxkIGJlIGF0dHJpYnV0ZWQgdG8gdGhlIHZhcmlhdGlvbnMgaW4gZGF0YXNldCBzaXplcy4gSW52ZXN0aWdhdGluZyB0aGUgaW1wYWN0IG9mIGRpZmZlcmVudCB0cmFpbmluZyBzZXQgc2l6ZXMgb24gbW9kZWwgcGVyZm9ybWFuY2Ugb2ZmZXJzIHZhbHVhYmxlIGluc2lnaHRzIGludG8gdGhlIGludHJpY2F0ZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBkYXRhIHNpemUgYW5kIGFjY3VyYWN5Lg0KDQpJbiB0aGUgY2FzZSBvZiBUcmVlIDIsIHdoZXJlIGEgbGFyZ2VyIHRyYWluaW5nIHNldCB3YXMgZW1wbG95ZWQgKDgwJSB0cmFpbmluZywgMjAlIHRlc3RpbmcpLCB0aGUgbW9kZWwgaGFkIHRoZSBvcHBvcnR1bml0eSB0byBncmFzcCBtb3JlIHJvYnVzdCBwYXR0ZXJucyBhbmQgcmVsYXRpb25zaGlwcyB3aXRoaW4gdGhlIGRhdGEuIEhvd2V2ZXIsIGl0IGlzIGNydWNpYWwgdG8gdW5kZXJzY29yZSB0aGUgbmVjZXNzaXR5IG9mIHN0cmlraW5nIGEgYmFsYW5jZSBiZXR3ZWVuIHRoZSBzaXplcyBvZiB0aGUgdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cy4gRm9yIFRyZWUgMywgd2l0aCBhIHJlbGF0aXZlbHkgc21hbGxlciB0ZXN0aW5nIHNldCAoOTAlIHRyYWluaW5nLCAxMCUgdGVzdGluZyksIHRoZSBhY2N1cmFjeSBlc3RpbWF0ZSBtaWdodCBiZSBsZXNzIHJlbGlhYmxlIGR1ZSB0byB0aGUgbGltaXRlZCBzYW1wbGUgc2l6ZSBpbiB0aGUgdGVzdGluZyBzZXQuDQoNCkluIHN1bW1hcnksIHRoZSB1dGlsaXphdGlvbiBvZiBpbmZvcm1hdGlvbiBnYWluIGFzIHRoZSBzZWxlY3Rpb24gbWVhc3VyZSwgY291cGxlZCB3aXRoIGRpZmZlcmVudCB0cmFpbmluZyBzZXQgc2l6ZXMsIHJlc3VsdGVkIGluIGNvbXBhcmFibGUgYWNjdXJhY3kgb3V0Y29tZXMuIEFjaGlldmluZyBhbiBvcHRpbWFsIGJhbGFuY2UgaW4gdGhlIHNpemVzIG9mIGJvdGggdHJhaW5pbmcgYW5kIHRlc3RpbmcgZGF0YXNldHMgcHJvdmVzIGVzc2VudGlhbCBmb3IgZW5zdXJpbmcgYWNjdXJhdGUgYW5kIGdlbmVyYWxpemFibGUgbW9kZWwgcGVyZm9ybWFuY2UuDQorLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBpbmZvcm1hdGlvbiBnYWluIHwgOTAgJXQgcmFpbmluZyBzZXQgfCA4MCAldCByYWluaW5nIHNldCB8IDcwICV0IHJhaW5pbmcgc2V0IHwNCnwgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgfCAxMCUgdGVzdGluZyBzZXQ6ICB8IDIwJSB0ZXN0aW5nIHNldDogIHwgMzAlIHRlc3Rpbmcgc2V0OiAgfA0KKzo9PT09PT09PT09PT09PT09Ois6PT09PT09PT09PT09PT09PT06Kzo9PT09PT09PT09PT09PT09PTorOj09PT09PT09PT09PT09PT09OisNCnwgKipBY2N1cmFjeSoqICAgICB8IDAuODcyICAgICAgICAgICAgIHwgMC45MDUgICAgICAgICAgICAgfCAwLjg5MyAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCAqKnByZWNpc2lvbioqICAgIHwgMC44NTYgICAgICAgICAgICAgfCAwLjg5OCAgICAgICAgICAgICB8IDAuODg3ICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICoqc2Vuc2l0aXZpdHkqKiAgfCAwLjg1NiAgICAgICAgICAgICB8IDAuODk4ICAgICAgICAgICAgIHwgMC44ODcgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgKipzcGVjaWZpY2l0eSoqICB8IDAuOTE5ICAgICAgICAgICAgIHwgMC45MzcgICAgICAgICAgICAgfCAwLjkyOSAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tKw0KDQoNCioqR2luaSBpbmRleCoqDQoNClRoZSBHaW5pIGluZGV4LCBpcyBhIG1lYXN1cmUgdXNlZCBpbiBkZWNpc2lvbiB0cmVlcywgc3BlY2lmaWNhbGx5IGluIHRoZSBDQVJUIChDbGFzc2lmaWNhdGlvbiBhbmQgUmVncmVzc2lvbiBUcmVlcykgYWxnb3JpdGhtLCB0byBxdWFudGlmeSBob3cgb2Z0ZW4gYSByYW5kb21seSBjaG9zZW4gZWxlbWVudCB3b3VsZCBiZSBpbmNvcnJlY3RseSBsYWJlbGVkIGlmIGl0IHdhcyByYW5kb21seSBsYWJlbGVkIGFjY29yZGluZyB0byB0aGUgZGlzdHJpYnV0aW9uIG9mIGxhYmVscyBpbiB0aGUgc3Vic2V0LiBJdCByZWZsZWN0cyB0aGUgcHJvYmFiaWxpdHkgb2YgYSBwYXJ0aWN1bGFyIHZhcmlhYmxlIGJlaW5nIHdyb25nbHkgY2xhc3NpZmllZCB3aGVuIGl0IGlzIHJhbmRvbWx5IGNob3Nlbi4NCg0KKioxLUdpbmkgaW5kZXgoODAlLDIwJSkqKg0KDQpJbnN0YWxsIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCg0KYGBge3J9DQppbnN0YWxsLnBhY2thZ2VzKCJycGFydCIpDQppbnN0YWxsLnBhY2thZ2VzKCJycGFydC5wbG90IikNCmluc3RhbGwucGFja2FnZXMoImNhVG9vbHMiKQ0KaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQ0KDQpgYGANCg0KTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzDQoNCmBgYHtyfQ0KbGlicmFyeShycGFydCkNCmxpYnJhcnkocnBhcnQucGxvdCkNCmxpYnJhcnkoY2FUb29scykNCmxpYnJhcnkoY2FyZXQpDQpgYGANCg0KU2V0IGEgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5DQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKQ0KDQpgYGANCg0KU3BsaXQgdGhlIGRhdGFzZXQsIDgwJSBmb3IgdHJhaW5pbmcsIDIwJSBmb3IgdGVzdGluZw0KDQpgYGB7cn0NCnNwbGl0IDwtIHNhbXBsZS5zcGxpdChIb2JieV9EYXRhJGBQcmVkaWN0ZWQgSG9iYnlgLCBTcGxpdFJhdGlvID0gMC44MCkNCmBgYA0KDQpDcmVhdGUgdGhlIHRyYWluaW5nIHNldCAoODAlIG9mIHRoZSBkYXRhKQ0KDQpgYGB7cn0NCnRyYWluaW5nX3NldCA8LSBzdWJzZXQoSG9iYnlfRGF0YSwgc3BsaXQgPT0gVFJVRSkNCmBgYA0KDQpDcmVhdGUgdGhlIHRlc3Qgc2V0ICgyMCUgb2YgdGhlIGRhdGEpDQoNCmBgYHtyfQ0KdGVzdF9zZXQgPC0gc3Vic2V0KEhvYmJ5X0RhdGEsIHNwbGl0ID09IEZBTFNFKQ0KDQpgYGANCg0KQnVpbGQgYSBkZWNpc2lvbiB0cmVlIG1vZGVsIG9uIHRoZSB0cmFpbmluZyBzZXQNCg0KYGBge3J9DQp0cmVlIDwtIHJwYXJ0KGBQcmVkaWN0ZWQgSG9iYnlgIH4gLiwgZGF0YSA9IHRyYWluaW5nX3NldCwgbWV0aG9kID0gJ2NsYXNzJykNCg0KYGBgDQoNCg0KTWFrZSBwcmVkaWN0aW9ucyBvbiB0aGUgdGVzdCBzZXQgdXNpbmcgdGhlIHRyZWUgbW9kZWwNCg0KYGBge3J9DQpwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KHRyZWUsIHRlc3Rfc2V0LCB0eXBlID0gImNsYXNzIikNCmBgYA0KDQpDb25mdXNpb24gbWF0cml4DQoNCmBgYHtyfQ0KY29uZl9tYXRyaXggPC0gdGFibGUoUHJlZGljdGVkID0gcHJlZGljdGlvbnMsIEFjdHVhbCA9IHRlc3Rfc2V0JGBQcmVkaWN0ZWQgSG9iYnlgKQ0KYGBgDQoNCkNhbGN1bGF0ZSBhY2N1cmFjeQ0KDQpgYGB7cn0NCmFjY3VyYWN5IDwtIHN1bShkaWFnKGNvbmZfbWF0cml4KSkgLyBzdW0oY29uZl9tYXRyaXgpDQpgYGANCg0KSW5pdGlhbGl6ZSB2ZWN0b3JzIHRvIGhvbGQgdGhlIG1ldHJpY3MgZm9yIGVhY2ggY2xhc3MNCg0KYGBge3J9DQpwcmVjaXNpb24gPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCnJlY2FsbCA8LSBudW1lcmljKGxlbmd0aCA9IG5yb3coY29uZl9tYXRyaXgpKQ0Kc3BlY2lmaWNpdHkgPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCmBgYA0KDQpDYWxjdWxhdGUgbWV0cmljcyBmb3IgZWFjaCBjbGFzcw0KDQpgYGB7cn0NCmZvciAoaSBpbiAxOm5yb3coY29uZl9tYXRyaXgpKSB7DQogIFRQIDwtIGNvbmZfbWF0cml4W2ksIGldDQogIEZQIDwtIHN1bShjb25mX21hdHJpeFssIGldKSAtIFRQDQogIEZOIDwtIHN1bShjb25mX21hdHJpeFtpLCBdKSAtIFRQDQogIFROIDwtIHN1bShjb25mX21hdHJpeCkgLSBUUCAtIEZQIC0gRk4NCiAgDQogIHByZWNpc2lvbltpXSA8LSBUUCAvIChUUCArIEZQKQ0KICByZWNhbGxbaV0gPC0gVFAgLyAoVFAgKyBGTikNCiAgc3BlY2lmaWNpdHlbaV0gPC0gVE4gLyAoVE4gKyBGUCkNCn0NCmBgYA0KDQpBdmVyYWdlIHRoZSBtZXRyaWNzIGlmIHlvdSB3YW50IGEgc2luZ2xlIHBlcmZvcm1hbmNlIG1lYXN1cmUNCg0KYGBge3J9DQphdmdfcHJlY2lzaW9uIDwtIG1lYW4ocHJlY2lzaW9uKQ0KYXZnX3JlY2FsbCA8LSBtZWFuKHJlY2FsbCkNCmF2Z19zcGVjaWZpY2l0eSA8LSBtZWFuKHNwZWNpZmljaXR5KQ0KYGBgDQoNCk91dHB1dCB0aGUgZXZhbHVhdGlvbiBtZXRyaWNzDQoNCmBgYHtyfQ0KcHJpbnQocGFzdGUoIk92ZXJhbGwgQWNjdXJhY3k6IiwgYWNjdXJhY3kpKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgUHJlY2lzaW9uOiIsIGF2Z19wcmVjaXNpb24pKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgUmVjYWxsIChTZW5zaXRpdml0eSk6IiwgYXZnX3JlY2FsbCkpDQpwcmludChwYXN0ZSgiQXZlcmFnZSBTcGVjaWZpY2l0eToiLCBhdmdfc3BlY2lmaWNpdHkpKQ0KYGBgDQoNCnRoZSBtZXRyaWNzIGZvciBlYWNoIGNsYXNzOg0KDQpgYGB7cn0NCm1ldHJpY3MgPC0gZGF0YS5mcmFtZSgNCiAgQ2xhc3MgPSByb3duYW1lcyhjb25mX21hdHJpeCksDQogIFByZWNpc2lvbiA9IHByZWNpc2lvbiwNCiAgUmVjYWxsID0gcmVjYWxsLA0KICBTcGVjaWZpY2l0eSA9IHNwZWNpZmljaXR5DQopDQpgYGANCg0KUHJpbnQgbWV0cmljcw0KDQpgYGB7cn0NCnByaW50KG1ldHJpY3MpDQoNCmBgYA0KDQpQbG90IHRoZSBkZWNpc2lvbiB0cmVlDQoNCmBgYHtyfQ0KcnBhcnQucGxvdCh0cmVlKQ0KYGBgDQoNCioqRGVjaXNpb24gVHJlZSBBbmFseXNpcyBVc2luZyBHaW5pIEluZGV4KDgwLzIwKToqKg0KIFRoZSBkZWNpc2lvbiB0cmVlIGRlbGluZWF0ZXMgaG9iYmllcyBpbnRvICdBY2FkZW1pY3MnICgxKSwgJ0FydHMnICgyKSwgYW5kICdTcG9ydHMnICgzKS4gV2l0aG91dCBhIHNwb3J0cyBob2JieSAoJ0NhcmVlcl9zcHJ0JyA9IDApLCB0aGUgbW9kZWwgc3VnZ2VzdHMgYSA2MiUgY2hhbmNlIG9mICdBY2FkZW1pY3MnLiBXaXRoIG5vIGFydHMgaG9iYnkgKCdGYW50X2FydHMnID0gMCkgYW5kICdXb25fYXJ0cycgYXQgMCBvciAyLCB0aGVyZSdzIGEgNDMlIGNoYW5jZSBvZiBhbiAnQWNhZGVtaWNzJyBjYXRlZ29yaXphdGlvbi4gQ29udmVyc2VseSwgZm9yIHRob3NlIHdpdGggYW4gYXJ0cyBob2JieSAoJ0ZhbnRfYXJ0cycgPSAxKSBhbmQgZnJlcXVlbnQgYXJ0cyBhY3Rpdml0aWVzICgnVGltZV9hcnQnIOKJpSAzKSwgVGhlc2UgbW9kZWwgc2hvdyBob3cgbGlrZWx5IHRoZSBtb2RlbCBpcyB0byBwcmVkaWN0IGVhY2ggaG9iYnkgYmFzZWQgb24gdGhlIGF0dHJpYnV0ZXMnIHNpZ25pZmljYW5jZSwgYXMgbGVhcm5lZCBmcm9tIHRoZSBkYXRhIHdpdGggYSA4MCUgdHJhaW5pbmcgcG9ydGlvbg0KDQoqKjItR2luaSBpbmRleCg5MCUsMTAlKSoqDQoNCkluc3RhbGwgbmVjZXNzYXJ5IGxpYnJhcmllcw0KDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoInJwYXJ0IikNCmluc3RhbGwucGFja2FnZXMoInJwYXJ0LnBsb3QiKQ0KaW5zdGFsbC5wYWNrYWdlcygiY2FUb29scyIpDQppbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQoNCmBgYA0KDQpMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCg0KYGBge3J9DQpsaWJyYXJ5KHJwYXJ0KQ0KbGlicmFyeShycGFydC5wbG90KQ0KbGlicmFyeShjYVRvb2xzKQ0KbGlicmFyeShjYXJldCkNCmBgYA0KDQpTZXQgYSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCg0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQoNCmBgYA0KDQpTcGxpdCB0aGUgZGF0YXNldCwgOTAlIGZvciB0cmFpbmluZywgMTAlIGZvciB0ZXN0aW5nDQoNCmBgYHtyfQ0Kc3BsaXQgPC0gc2FtcGxlLnNwbGl0KEhvYmJ5X0RhdGEkYFByZWRpY3RlZCBIb2JieWAsIFNwbGl0UmF0aW8gPSAwLjkwKQ0KYGBgDQoNCkNyZWF0ZSB0aGUgdHJhaW5pbmcgc2V0ICg5MCUgb2YgdGhlIGRhdGEpDQoNCmBgYHtyfQ0KdHJhaW5pbmdfc2V0IDwtIHN1YnNldChIb2JieV9EYXRhLCBzcGxpdCA9PSBUUlVFKQ0KYGBgDQoNCkNyZWF0ZSB0aGUgdGVzdCBzZXQgKDEwJSBvZiB0aGUgZGF0YSkNCg0KYGBge3J9DQp0ZXN0X3NldCA8LSBzdWJzZXQoSG9iYnlfRGF0YSwgc3BsaXQgPT0gRkFMU0UpDQoNCmBgYA0KDQpCdWlsZCBhIGRlY2lzaW9uIHRyZWUgbW9kZWwgb24gdGhlIHRyYWluaW5nIHNldA0KDQpgYGB7cn0NCnRyZWUgPC0gcnBhcnQoYFByZWRpY3RlZCBIb2JieWAgfiAuLCBkYXRhID0gdHJhaW5pbmdfc2V0LCBtZXRob2QgPSAnY2xhc3MnKQ0KDQpgYGANCg0KDQpNYWtlIHByZWRpY3Rpb25zIG9uIHRoZSB0ZXN0IHNldCB1c2luZyB0aGUgdHJlZSBtb2RlbA0KDQpgYGB7cn0NCnByZWRpY3Rpb25zIDwtIHByZWRpY3QodHJlZSwgdGVzdF9zZXQsIHR5cGUgPSAiY2xhc3MiKQ0KYGBgDQoNCkNvbmZ1c2lvbiBtYXRyaXgNCg0KYGBge3J9DQpjb25mX21hdHJpeCA8LSB0YWJsZShQcmVkaWN0ZWQgPSBwcmVkaWN0aW9ucywgQWN0dWFsID0gdGVzdF9zZXQkYFByZWRpY3RlZCBIb2JieWApDQpgYGANCg0KQ2FsY3VsYXRlIGFjY3VyYWN5DQoNCmBgYHtyfQ0KYWNjdXJhY3kgPC0gc3VtKGRpYWcoY29uZl9tYXRyaXgpKSAvIHN1bShjb25mX21hdHJpeCkNCmBgYA0KDQpJbml0aWFsaXplIHZlY3RvcnMgdG8gaG9sZCB0aGUgbWV0cmljcyBmb3IgZWFjaCBjbGFzcw0KDQpgYGB7cn0NCnByZWNpc2lvbiA8LSBudW1lcmljKGxlbmd0aCA9IG5yb3coY29uZl9tYXRyaXgpKQ0KcmVjYWxsIDwtIG51bWVyaWMobGVuZ3RoID0gbnJvdyhjb25mX21hdHJpeCkpDQpzcGVjaWZpY2l0eSA8LSBudW1lcmljKGxlbmd0aCA9IG5yb3coY29uZl9tYXRyaXgpKQ0KYGBgDQoNCkNhbGN1bGF0ZSBtZXRyaWNzIGZvciBlYWNoIGNsYXNzDQoNCmBgYHtyfQ0KZm9yIChpIGluIDE6bnJvdyhjb25mX21hdHJpeCkpIHsNCiAgVFAgPC0gY29uZl9tYXRyaXhbaSwgaV0NCiAgRlAgPC0gc3VtKGNvbmZfbWF0cml4WywgaV0pIC0gVFANCiAgRk4gPC0gc3VtKGNvbmZfbWF0cml4W2ksIF0pIC0gVFANCiAgVE4gPC0gc3VtKGNvbmZfbWF0cml4KSAtIFRQIC0gRlAgLSBGTg0KICANCiAgcHJlY2lzaW9uW2ldIDwtIFRQIC8gKFRQICsgRlApDQogIHJlY2FsbFtpXSA8LSBUUCAvIChUUCArIEZOKQ0KICBzcGVjaWZpY2l0eVtpXSA8LSBUTiAvIChUTiArIEZQKQ0KfQ0KYGBgDQoNCkF2ZXJhZ2UgdGhlIG1ldHJpY3MgaWYgeW91IHdhbnQgYSBzaW5nbGUgcGVyZm9ybWFuY2UgbWVhc3VyZQ0KDQpgYGB7cn0NCmF2Z19wcmVjaXNpb24gPC0gbWVhbihwcmVjaXNpb24pDQphdmdfcmVjYWxsIDwtIG1lYW4ocmVjYWxsKQ0KYXZnX3NwZWNpZmljaXR5IDwtIG1lYW4oc3BlY2lmaWNpdHkpDQpgYGANCg0KT3V0cHV0IHRoZSBldmFsdWF0aW9uIG1ldHJpY3MNCg0KYGBge3J9DQpwcmludChwYXN0ZSgiT3ZlcmFsbCBBY2N1cmFjeToiLCBhY2N1cmFjeSkpDQpwcmludChwYXN0ZSgiQXZlcmFnZSBQcmVjaXNpb246IiwgYXZnX3ByZWNpc2lvbikpDQpwcmludChwYXN0ZSgiQXZlcmFnZSBSZWNhbGwgKFNlbnNpdGl2aXR5KToiLCBhdmdfcmVjYWxsKSkNCnByaW50KHBhc3RlKCJBdmVyYWdlIFNwZWNpZmljaXR5OiIsIGF2Z19zcGVjaWZpY2l0eSkpDQpgYGANCg0KdGhlIG1ldHJpY3MgZm9yIGVhY2ggY2xhc3M6DQoNCmBgYHtyfQ0KbWV0cmljcyA8LSBkYXRhLmZyYW1lKA0KICBDbGFzcyA9IHJvd25hbWVzKGNvbmZfbWF0cml4KSwNCiAgUHJlY2lzaW9uID0gcHJlY2lzaW9uLA0KICBSZWNhbGwgPSByZWNhbGwsDQogIFNwZWNpZmljaXR5ID0gc3BlY2lmaWNpdHkNCikNCmBgYA0KDQpQcmludCBtZXRyaWNzDQoNCmBgYHtyfQ0KcHJpbnQobWV0cmljcykNCg0KYGBgDQoNCg0KUGxvdCB0aGUgZGVjaXNpb24gdHJlZQ0KDQpgYGB7cn0NCnJwYXJ0LnBsb3QodHJlZSkNCmBgYA0KDQoqKkRlY2lzaW9uIFRyZWUgQW5hbHlzaXMgVXNpbmcgR2luaSBJbmRleCg5MC8xMCk6KioNCg0KVGhlIGRlY2lzaW9uIHRyZWUgY2xhc3NpZmllcyBob2JiaWVzIGludG8gJ0FjYWRlbWljcycgKDEpLCAnQXJ0cycgKDIpLCBhbmQgJ1Nwb3J0cycgKDMpLiBBIGxhY2sgb2YgYSBzcG9ydHMgaG9iYnkgKCdDYXJlZXJfc3BydCcgPSAwKSBsZWFkcyB0byBhIDYzJSBjaGFuY2Ugb2YgZmFsbGluZyBpbnRvICdBY2FkZW1pY3MnLiBJZiBzb21lb25lIGlzIG5vdCBlbmdhZ2VkIGluIGFuIGFydHMgaG9iYnkgKCdGYW50X2FydHMnID0gMCkgYW5kICdXb25fYXJ0cycgaXMgMCBvciAyLCB0aGVyZSdzIGEgNDMlIHByb2JhYmlsaXR5IG9mIGFuICdBY2FkZW1pY3MnIGNhdGVnb3JpemF0aW9uLiBGb3IgaW5kaXZpZHVhbHMgZW5nYWdlZCBpbiBhbiBhcnRzIGhvYmJ5ICgnRmFudF9hcnRzJyA9IDEpIHdpdGggYSBoaWdoIGxldmVsIG9mIGFydHMgYWN0aXZpdHkgKCdUaW1lX2FydCcg4omlIDMpLCB0aGUgbGlrZWxpaG9vZCBvZiBhICdTcG9ydHMnIGNsYXNzaWZpY2F0aW9uIGlzIDI4JS4gVGhlc2UgbW9kZWwgc2hvdyBob3cgbGlrZWx5IHRoZSBtb2RlbCBpcyB0byBwcmVkaWN0IGVhY2ggaG9iYnkgYmFzZWQgb24gdGhlIGF0dHJpYnV0ZXMnIHNpZ25pZmljYW5jZSwgYXMgbGVhcm5lZCBmcm9tIHRoZSBkYXRhIHdpdGggYSA5MCUgdHJhaW5pbmcgcG9ydGlvbi4NCg0KDQoqKjMtR2luaSBpbmRleCg3MCUsMzAlKSoqDQoNCkluc3RhbGwgbmVjZXNzYXJ5IGxpYnJhcmllcw0KDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoInJwYXJ0IikNCmluc3RhbGwucGFja2FnZXMoInJwYXJ0LnBsb3QiKQ0KaW5zdGFsbC5wYWNrYWdlcygiY2FUb29scyIpDQppbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQoNCmBgYA0KDQpMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCg0KYGBge3J9DQpsaWJyYXJ5KHJwYXJ0KQ0KbGlicmFyeShycGFydC5wbG90KQ0KbGlicmFyeShjYVRvb2xzKQ0KbGlicmFyeShjYXJldCkNCmBgYA0KDQpTZXQgYSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCg0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQoNCmBgYA0KDQpTcGxpdCB0aGUgZGF0YXNldCwgNzAlIGZvciB0cmFpbmluZywgMzAlIGZvciB0ZXN0aW5nDQoNCmBgYHtyfQ0Kc3BsaXQgPC0gc2FtcGxlLnNwbGl0KEhvYmJ5X0RhdGEkYFByZWRpY3RlZCBIb2JieWAsIFNwbGl0UmF0aW8gPSAwLjcwKQ0KYGBgDQoNCkNyZWF0ZSB0aGUgdHJhaW5pbmcgc2V0ICg3MCUgb2YgdGhlIGRhdGEpDQoNCmBgYHtyfQ0KdHJhaW5pbmdfc2V0IDwtIHN1YnNldChIb2JieV9EYXRhLCBzcGxpdCA9PSBUUlVFKQ0KYGBgDQoNCkNyZWF0ZSB0aGUgdGVzdCBzZXQgKDIwJSBvZiB0aGUgZGF0YSkNCg0KYGBge3J9DQp0ZXN0X3NldCA8LSBzdWJzZXQoSG9iYnlfRGF0YSwgc3BsaXQgPT0gRkFMU0UpDQoNCmBgYA0KDQpCdWlsZCBhIGRlY2lzaW9uIHRyZWUgbW9kZWwgb24gdGhlIHRyYWluaW5nIHNldA0KDQpgYGB7cn0NCnRyZWUgPC0gcnBhcnQoYFByZWRpY3RlZCBIb2JieWAgfiAuLCBkYXRhID0gdHJhaW5pbmdfc2V0LCBtZXRob2QgPSAnY2xhc3MnKQ0KDQpgYGANCg0KTWFrZSBwcmVkaWN0aW9ucyBvbiB0aGUgdGVzdCBzZXQgdXNpbmcgdGhlIHRyZWUgbW9kZWwNCg0KYGBge3J9DQpwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KHRyZWUsIHRlc3Rfc2V0LCB0eXBlID0gImNsYXNzIikNCmBgYA0KDQpDb25mdXNpb24gbWF0cml4DQoNCmBgYHtyfQ0KY29uZl9tYXRyaXggPC0gdGFibGUoUHJlZGljdGVkID0gcHJlZGljdGlvbnMsIEFjdHVhbCA9IHRlc3Rfc2V0JGBQcmVkaWN0ZWQgSG9iYnlgKQ0KYGBgDQoNCkNhbGN1bGF0ZSBhY2N1cmFjeQ0KDQpgYGB7cn0NCmFjY3VyYWN5IDwtIHN1bShkaWFnKGNvbmZfbWF0cml4KSkgLyBzdW0oY29uZl9tYXRyaXgpDQpgYGANCg0KSW5pdGlhbGl6ZSB2ZWN0b3JzIHRvIGhvbGQgdGhlIG1ldHJpY3MgZm9yIGVhY2ggY2xhc3MNCg0KYGBge3J9DQpwcmVjaXNpb24gPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCnJlY2FsbCA8LSBudW1lcmljKGxlbmd0aCA9IG5yb3coY29uZl9tYXRyaXgpKQ0Kc3BlY2lmaWNpdHkgPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCmBgYA0KDQpDYWxjdWxhdGUgbWV0cmljcyBmb3IgZWFjaCBjbGFzcw0KDQpgYGB7cn0NCmZvciAoaSBpbiAxOm5yb3coY29uZl9tYXRyaXgpKSB7DQogIFRQIDwtIGNvbmZfbWF0cml4W2ksIGldDQogIEZQIDwtIHN1bShjb25mX21hdHJpeFssIGldKSAtIFRQDQogIEZOIDwtIHN1bShjb25mX21hdHJpeFtpLCBdKSAtIFRQDQogIFROIDwtIHN1bShjb25mX21hdHJpeCkgLSBUUCAtIEZQIC0gRk4NCiAgDQogIHByZWNpc2lvbltpXSA8LSBUUCAvIChUUCArIEZQKQ0KICByZWNhbGxbaV0gPC0gVFAgLyAoVFAgKyBGTikNCiAgc3BlY2lmaWNpdHlbaV0gPC0gVE4gLyAoVE4gKyBGUCkNCn0NCmBgYA0KDQpBdmVyYWdlIHRoZSBtZXRyaWNzIGlmIHlvdSB3YW50IGEgc2luZ2xlIHBlcmZvcm1hbmNlIG1lYXN1cmUNCg0KYGBge3J9DQphdmdfcHJlY2lzaW9uIDwtIG1lYW4ocHJlY2lzaW9uKQ0KYXZnX3JlY2FsbCA8LSBtZWFuKHJlY2FsbCkNCmF2Z19zcGVjaWZpY2l0eSA8LSBtZWFuKHNwZWNpZmljaXR5KQ0KYGBgDQoNCk91dHB1dCB0aGUgZXZhbHVhdGlvbiBtZXRyaWNzDQoNCmBgYHtyfQ0KcHJpbnQocGFzdGUoIk92ZXJhbGwgQWNjdXJhY3k6IiwgYWNjdXJhY3kpKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgUHJlY2lzaW9uOiIsIGF2Z19wcmVjaXNpb24pKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgUmVjYWxsIChTZW5zaXRpdml0eSk6IiwgYXZnX3JlY2FsbCkpDQpwcmludChwYXN0ZSgiQXZlcmFnZSBTcGVjaWZpY2l0eToiLCBhdmdfc3BlY2lmaWNpdHkpKQ0KYGBgDQoNCnRoZSBtZXRyaWNzIGZvciBlYWNoIGNsYXNzOg0KDQpgYGB7cn0NCm1ldHJpY3MgPC0gZGF0YS5mcmFtZSgNCiAgQ2xhc3MgPSByb3duYW1lcyhjb25mX21hdHJpeCksDQogIFByZWNpc2lvbiA9IHByZWNpc2lvbiwNCiAgUmVjYWxsID0gcmVjYWxsLA0KICBTcGVjaWZpY2l0eSA9IHNwZWNpZmljaXR5DQopDQpgYGANCg0KUHJpbnQgbWV0cmljcw0KDQpgYGB7cn0NCnByaW50KG1ldHJpY3MpDQoNCmBgYA0KDQpQbG90IHRoZSBkZWNpc2lvbiB0cmVlDQoNCmBgYHtyfQ0KcnBhcnQucGxvdCh0cmVlKQ0KYGBgDQoNCioqRGVjaXNpb24gVHJlZSBBbmFseXNpcyBVc2luZyBHaW5pIEluZGV4KDcwLzMwKToqKg0KDQpUaGUgZGVjaXNpb24gdHJlZSBzb3J0cyBob2JiaWVzIGludG8gJ0FjYWRlbWljcycgKDEpLCAnQXJ0cycgKDIpLCBhbmQgJ1Nwb3J0cycgKDMpLiBBIG5vbi1zcG9ydHMgaG9iYnkgKCdDYXJlZXJfc3BydCcgPSAwKSByZXN1bHRzIGluIGEgNjMlIHByb2JhYmlsaXR5IG9mIGFuICdBY2FkZW1pY3MnIGNhdGVnb3JpemF0aW9uLiBJZiAnRmFudF9hcnRzJyBpcyAwIGFuZCAnV29uX2FydHMnIGlzIDAgb3IgMiwgdGhlcmUncyBhIDQzJSBjaGFuY2Ugb2YgYmVpbmcgY2xhc3NpZmllZCBhcyAnQWNhZGVtaWNzJy4gQ29udmVyc2VseSwgZm9yIHRob3NlIGludm9sdmVkIGluIGFuIGFydHMgaG9iYnkgKCdGYW50X2FydHMnID0gMSkgd2l0aCBzaWduaWZpY2FudCBhcnRzIGFjdGl2aXR5ICgnVGltZV9hcnQnIOKJpSAzKSwgdGhlIG1vZGVsIGluZGljYXRlcyBhIDI4JSBwcm9iYWJpbGl0eSBvZiBhICdTcG9ydHMnIGhvYmJ5LiBUaGlzIGRlY2lzaW9uIHRyZWUgZGVtb25zdHJhdGVzIHRoZSBsaWtlbGlob29kIG9mIHByZWRpY3RpbmcgZWFjaCBob2JieSBiYXNlZCBvbiB0aGUgaW1wb3J0YW5jZSBvZiB0aGUgYXR0cmlidXRlcywgYXMgZGV0ZXJtaW5lZCBmcm9tIHRoZSBkYXRhIHRyYWluZWQgd2l0aCBhIDcwJSBwb3J0aW9uLg0KDQoqKkNvbXBhcmluZyBEZWNpc2lvbiBUcmVlIFJlc3VsdHMgVXNpbmcgR2luaSBJbmRleDoqKg0KDQpBY3Jvc3MgVGhyZWUgVHJhaW5pbmctVGVzdCBTaXplczogVGhlIHJlc3VsdHMgb2YgdGhlIGRlY2lzaW9uIHRyZWVzIGZyb20gdGhlIDkwLzEwLCA4MC8yMCwgYW5kIDcwLzMwIGRhdGFzZXQgc3BsaXRzLCB0aGVyZSBpcyBhIGNvbnNpc3RlbnQgcGF0dGVybjogJ0NhcmVlcl9zcHJ0JyBpcyBhbHdheXMgdGhlIHJvb3Qgbm9kZSwgYW5kIHRoZSBzdWJzZXF1ZW50IHNwbGl0cyBvbiAnV29uX2FydHMnIGFuZCAnRmFudF9hcnRzJyBhcmUgdGhlIHNhbWUgYWNyb3NzIGFsbCB0cmVlcy4gVGhpcyBjb25zaXN0ZW5jeSBpbiB0cmVlIHN0cnVjdHVyZSBhbmQgdGhlIHByb2JhYmlsaXRpZXMgZm9yIHByZWRpY3RpbmcgJ0FjYWRlbWljcycgYW5kICdTcG9ydHMnIGFjcm9zcyBkaWZmZXJlbnQgc3BsaXRzIHN1Z2dlc3QgYSBzdGFibGUgYW5kIHJvYnVzdCBtb2RlbCB0aGF0IGlzIHJlbGlhYmxlIHJlZ2FyZGxlc3Mgb2YgdGhlIHRyYWluaW5nIHNldCBzaXplLg0KDQp0aGUgYWNjdXJhY2llcyBvZiB0aHJlZSBkYXRhIHNwbGl0cyByZXZlYWxzIGRpc3RpbmN0IG91dGNvbWVzOiB0aGUgKDkwLDEwKSBzcGxpdCBsZWFkcyB3aXRoIHRoZSBoaWdoZXN0IGFjY3VyYWN5IGF0IDAuOTE4NzUsIHN1Z2dlc3RpbmcgdGhhdCBhIGxhcmdlciB0cmFpbmluZyBwb3J0aW9uIGlzIG1vcmUgZWZmZWN0aXZlIGluIHRoaXMgY2FzZS4gVGhlICg3MCwzMCkgc3BsaXQgZm9sbG93cyB3aXRoIGFuIGFjY3VyYWN5IG9mIDAuOTEwNjAsIHNob3dpbmcgc3Ryb25nIHBlcmZvcm1hbmNlIGV2ZW4gd2l0aCBhIGxhcmdlciB0ZXN0IHNldC4gSG93ZXZlciwgdGhlIGNvbW1vbmx5IHVzZWQgKDgwLDIwKSBzcGxpdCBsYWdzIHNsaWdodGx5IGJlaGluZCwgYWNoaWV2aW5nIGFuIGFjY3VyYWN5IG9mIDAuOTA2MjUuIFRoaXMgY29tcGFyaXNvbiBoaWdobGlnaHRzIHRoZSBpbXBhY3Qgb2YgdmFyeWluZyB0cmFpbmluZyBhbmQgdGVzdGluZyBwcm9wb3J0aW9ucyBvbiBtb2RlbCBhY2N1cmFjeS4NCg0KDQorLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IEdpbmkgaW5kZXggICAgICB8IDkwICV0IHJhaW5pbmcgc2V0IDEwJSB0ZXN0aW5nIHNldDogfCA4MCAldCByYWluaW5nIHNldCAyMCUgdGVzdGluZyBzZXQ6IHwgNzAgJXQgcmFpbmluZyBzZXQgMzAlIHRlc3Rpbmcgc2V0OiB8DQorOj09PT09PT09PT09PT09PTorOj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT06Kzo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ois6PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PTorDQp8ICoqQWNjdXJhY3kqKiAgICB8IDAuOTE4NzUgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwLjkwNiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMC45MTEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICoqcHJlY2lzaW9uKiogICB8IDAuOTE4NTAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwLjkwNSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMC45MDkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICoqc2Vuc2l0aXZpdHkqKiB8IDAuOTE5MjcgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwLjkwOSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMC45MTIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICoqc3BlY2lmaWNpdHkqKiB8IDAuOTU3OCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwLjk1MiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMC45NTQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQoNCg0KKipHYWluIFJhdGlvKioNClRoZSB0aGlyZCBjcml0ZXJpb24gZW1wbG95ZWQgZm9yIGJ1aWxkaW5nIHRoZSBkZWNpc2lvbiB0cmVlIGlzIEdhaW4gUmF0aW8uIEdhaW4gUmF0aW8gc3RhbmRzIG91dCBhcyBhIHNpZ25pZmljYW50IG1ldHJpYyBpbiBkZWNpc2lvbiB0cmVlIGFsZ29yaXRobXMsIGVzcGVjaWFsbHkgaW4gc2NlbmFyaW9zIGludm9sdmluZyBjYXRlZ29yaWNhbCB0YXJnZXQgdmFyaWFibGVzLiBJdCBub3JtYWxpemVzIHRoZSByZWR1Y3Rpb24gaW4gZW50cm9weSBieSB0YWtpbmcgaW50byBhY2NvdW50IHRoZSBwb3RlbnRpYWwgaW5mb3JtYXRpb24gY29udGVudCBvZiB0aGUgZmVhdHVyZS4gVGhpcyBub3JtYWxpemF0aW9uIHByb2Nlc3MgbWFrZXMgR2FpbiBSYXRpbyBwYXJ0aWN1bGFybHkgc3VpdGFibGUgZm9yIGRhdGFzZXRzIHdpdGggY2F0ZWdvcmljYWwgdGFyZ2V0IHZhcmlhYmxlcy4gQnkgZmFjdG9yaW5nIGluIHRoZSBpbnRyaW5zaWMgaW5mb3JtYXRpb24gb2YgYSBzcGxpdCwgR2FpbiBSYXRpbyBlZmZlY3RpdmVseSBtaXRpZ2F0ZXMgYmlhcyB0b3dhcmRzIGZlYXR1cmVzIHdpdGggaGlnaGVyIGxldmVscywgZW5zdXJpbmcgYSBtb3JlIGJhbGFuY2VkIGV2YWx1YXRpb24gb2YgZGlmZmVyZW50IGF0dHJpYnV0ZXMuDQoNCioqMS1HYWluIHJhdGlvKDkwJSwxMCUpKioNCg0KSW5zdGFsbCBuZWNlc3NhcnkgbGlicmFyaWVzDQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygiQzUwIikNCmluc3RhbGwucGFja2FnZXMoInByaW50ciIpDQppbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQpgYGANCg0KTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzDQoNCmBgYHtyfQ0KDQpsaWJyYXJ5KEM1MCkNCmxpYnJhcnkocHJpbnRyKQ0KbGlicmFyeShjYXJldCkNCmBgYA0KDQpTZXQgYSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCg0KYGBge3J9DQpzZXQuc2VlZCgxOTU4KQ0KDQpgYGANCg0KU3BsaXR0aW5nIHRoZSBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3Qgc2V0cw0KDQpgYGB7cn0NCnRyYWluX2luZGljZXMgPC0gc2FtcGxlKDE6bnJvdyhIb2JieV9EYXRhKSwgMC45ICogbnJvdyhIb2JieV9EYXRhKSkNCkhvYmJ5LnRyYWluIDwtIEhvYmJ5X0RhdGFbdHJhaW5faW5kaWNlcywgXQ0KSG9iYnkudGVzdCA8LSBIb2JieV9EYXRhWy10cmFpbl9pbmRpY2VzLCBdDQoNCmBgYA0KDQpUcmFpbmluZyB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbA0KDQpgYGB7cn0NCm1vZGVsIDwtIEM1LjAoYFByZWRpY3RlZCBIb2JieWAgfiAuLCBkYXRhID0gSG9iYnkudHJhaW4sIGNvbnRyb2wgPSBDNS4wQ29udHJvbChDRiA9IDAuMDEpKQ0KDQpgYGANCg0KTWFraW5nIHByZWRpY3Rpb25zIG9uIHRoZSB0ZXN0IHNldA0KDQpgYGB7cn0NCnByZWRpY3Rpb25zIDwtIHByZWRpY3QobW9kZWwsIG5ld2RhdGEgPSBIb2JieS50ZXN0LCB0eXBlID0gJ2NsYXNzJykNCmBgYA0KDQpDcmVhdGUgYSBjb25mdXNpb24gbWF0cml4IGZyb20gdGhlIHByZWRpY3Rpb25zIGFuZCBhY3R1YWwgdmFsdWVzDQoNCmBgYHtyfQ0KY29uZl9tYXRyaXggPC0gdGFibGUoUHJlZGljdGVkID0gcHJlZGljdGlvbnMsIEFjdHVhbCA9IEhvYmJ5LnRlc3QkYFByZWRpY3RlZCBIb2JieWApDQpgYGANCg0KQ2FsY3VsYXRlIGFuZCBwcmludCB0aGUgYWNjdXJhY3kgb2YgdGhlIG1vZGVsDQoNCmBgYHtyfQ0KYWNjdXJhY3kgPC0gc3VtKGRpYWcoY29uZl9tYXRyaXgpKSAvIHN1bShjb25mX21hdHJpeCkNCnByaW50KHBhc3RlKCdBY2N1cmFjeSBvbiB0ZXN0IGRhdGEgaXM6JywgYWNjdXJhY3kpKQ0KYGBgDQoNCkluaXRpYWxpemUgdmVjdG9ycyB0byBob2xkIHRoZSBtZXRyaWNzIGZvciBlYWNoIGNsYXNzDQoNCmBgYHtyfQ0KcHJlY2lzaW9uIDwtIG51bWVyaWMobGVuZ3RoID0gbnJvdyhjb25mX21hdHJpeCkpDQpyZWNhbGwgPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCnNwZWNpZmljaXR5IDwtIG51bWVyaWMobGVuZ3RoID0gbnJvdyhjb25mX21hdHJpeCkpDQpgYGANCg0KQ2FsY3VsYXRlIG1ldHJpY3MgZm9yIGVhY2ggY2xhc3MNCg0KYGBge3J9DQpmb3IgKGkgaW4gMTpucm93KGNvbmZfbWF0cml4KSkgew0KICBUUCA8LSBjb25mX21hdHJpeFtpLCBpXQ0KICBGUCA8LSBzdW0oY29uZl9tYXRyaXhbLCBpXSkgLSBUUA0KICBGTiA8LSBzdW0oY29uZl9tYXRyaXhbaSwgXSkgLSBUUA0KICBUTiA8LSBzdW0oY29uZl9tYXRyaXgpIC0gVFAgLSBGUCAtIEZODQogIA0KICBwcmVjaXNpb25baV0gPC0gVFAgLyAoVFAgKyBGUCkNCiAgcmVjYWxsW2ldIDwtIFRQIC8gKFRQICsgRk4pDQogIHNwZWNpZmljaXR5W2ldIDwtIFROIC8gKFROICsgRlApDQp9DQpgYGANCg0KQXZlcmFnZSB0aGUgbWV0cmljcyBpZiB5b3Ugd2FudCBhIHNpbmdsZSBwZXJmb3JtYW5jZSBtZWFzdXJlDQoNCmBgYHtyfQ0KYXZnX3ByZWNpc2lvbiA8LSBtZWFuKHByZWNpc2lvbikNCmF2Z19yZWNhbGwgPC0gbWVhbihyZWNhbGwpDQphdmdfc3BlY2lmaWNpdHkgPC0gbWVhbihzcGVjaWZpY2l0eSkNCmBgYA0KDQpPdXRwdXQgdGhlIGV2YWx1YXRpb24gbWV0cmljcw0KDQpgYGB7cn0NCnByaW50KHBhc3RlKCJPdmVyYWxsIEFjY3VyYWN5OiIsIGFjY3VyYWN5KSkNCnByaW50KHBhc3RlKCJBdmVyYWdlIFByZWNpc2lvbjoiLCBhdmdfcHJlY2lzaW9uKSkNCnByaW50KHBhc3RlKCJBdmVyYWdlIFJlY2FsbCAoU2Vuc2l0aXZpdHkpOiIsIGF2Z19yZWNhbGwpKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgU3BlY2lmaWNpdHk6IiwgYXZnX3NwZWNpZmljaXR5KSkNCmBgYA0KDQpwcmludCB0aGUgbWV0cmljcyBmb3IgZWFjaCBjbGFzczoNCg0KYGBge3J9DQoNCm1ldHJpY3MgPC0gZGF0YS5mcmFtZSgNCiAgQ2xhc3MgPSByb3duYW1lcyhjb25mX21hdHJpeCksDQogIFByZWNpc2lvbiA9IHByZWNpc2lvbiwNCiAgUmVjYWxsID0gcmVjYWxsLA0KICBTcGVjaWZpY2l0eSA9IHNwZWNpZmljaXR5DQopDQpgYGANCg0KUHJpbnQgbWV0cmljcw0KDQpgYGB7cn0NCnByaW50KG1ldHJpY3MpDQpgYGANCg0KR2VuZXJhdGUgYW5kIHByaW50IGFkZGl0aW9uYWwgcGVyZm9ybWFuY2UgbWV0cmljcyB1c2luZyBjYXJldCBwYWNrYWdlDQoNCmBgYHtyfQ0KY29uZnVzaW9uTWF0cml4KHByZWRpY3Rpb25zLCBIb2JieS50ZXN0JGBQcmVkaWN0ZWQgSG9iYnlgKQ0KYGBgDQoNClBsb3QgdGhlIGRlY2lzaW9uIHRyZWUNCg0KYGBge3J9DQpwbG90KG1vZGVsKQ0KYGBgDQoNCioqRGVjaXNpb24gVHJlZSBBbmFseXNpcyBVc2luZyBHYWluIFJhdGlvKDkwJS8xMCUpOioqDQoNCkluIEZpcnN0IFRyZWUgLHdlIGRldmlkZSBkYXRhc2V0IGludG8gdHJhaW5pbmcgc2V0IGFuZCB0ZXN0IHNldCB3aXRoIHNpemUoJTkwLCUxMCkgcmVzcGVjdGl2ZWx5LiBBcyB5b3UgY2FuIHNlZSBpbiB0aGUgZmlndXJlLCB0aGUgcm9vdCBub2RlIGlzICJDYXJlZXJfc3BydCIgLCBjbGFzcyAxKCJBY2FkZW1pY3MiKSwgY2xhc3MgMigiQXJ0cyIpLCBhbmQgY2xhc3MgMyAoIlNwb3J0cyIpLg0KDQpOb2RlICJDYXJlZXJfc3BydCIgVGhlIGZpcnN0IGRlY2lzaW9uIGlzIGJhc2VkIG9uIHdoZXRoZXIgdGhlIHZhbHVlIG9mIHRoZSAiQ2FyZWVyX3NwcnQiIGF0dHJpYnV0ZSBpcyAwLiB0aGVuIGNoZWNrIGlmICJXb25fYXJ0cyIgaXMgZWl0aGVyIDAgb3IgMi5JZiIgV29uX2FydHMiIGlzIDAgb3IgMiAsIHByZWRpY3QiQWNhZGVtaWMiLnRoZW4gY2hlY2sgSWYgIkZhbnRfYXJ0cyIgaXMgMSAiYW5kIFdvbl9hcnRzIiBpcyAxLCBwcmVkaWN0ICJBcnRzIi5JZiAiRmFudF9hcnRzIiBpcyAwIGFuZCBXb25fYXJ0cyBpcyAwIG9yIDIsIHRoZW4gY2hlY2sgaWYgT2x5bXBpYWRfUGFydGljaXBhdGlvbiBpcyAxLklmIE9seW1waWFkX1BhcnRpY2lwYXRpb24gaXMgMSBwcmVkaWN0ICJBY2FkZW1pY3MiLihXaGVuIE9seW1waWFkX1BhcnRpY2lwYXRpb24gaXMgMCkgSWYgIk9seW1waWFkX1BhcnRpY2lwYXRpb24iIGlzIDAgYW5kICJGYW50X2FydHMiIGlzIDAsIHRoZW4gY2hlY2sgaWYgIkdyYXNwX3BvdyIgaXMgbGVzcyB0aGFuIG9yIGVxdWFsIHRvIDQgLCBwcmVkaWN0IGNsYXNzIkFydHMiLldoZW4gQ2FyZWVyX3NwcnQgaXMgMSwgdGhlbiBjaGVjayBpZiAiRmFudF9hcnRzIiBpcyAwIHRoZW4gcHJlZGljdCAiU3BvcnRzIi4gSWYgIkZhbnRfYXJ0cyIgaXMgMSwgdGhlbiBjaGVjayBpZiAiVGltZV9hcnQiIGlzIGdyZWF0ZXIgdGhhbiAyLmNoZWNrIGlmICJUaW1lX2FydCIgaXMgbGVzcyB0aGFuIG9yIGVxdWFsIHRvIDIsIHRoZW4gY2hlY2sgaWYgIkFjdF9zcHJ0IiBpcyAxIG9yIDAuSWYgQWN0X3NwcnQgaXMgMSwgcHJlZGljdCAiU3BvcnRzIi4gSWYgQWN0X3NwcnQgaXMgMCwgdGhlbiBjaGVjayBpZiBPbHltcGlhZF9QYXJ0aWNpcGF0aW9uIGlzIDAgcHJlZGljdCAiQXJ0cyIuIElmICJPbHltcGlhZF9QYXJ0aWNpcGF0aW9uIiBpcyAxLCBwcmVkaWN0ICJBY2FkZW1pY3MiLldoZW4gVGltZV9hcnQgaXMgZ3JlYXRlciB0aGFuIDIgLElmIFdvbl9hcnRzIGlzIDAsIHByZWRpY3QgY2xhc3MgIlNwb3J0cyIuDQoNCg0KKioyLUdhaW4gcmF0aW8oODAlLDIwJSkqKg0KDQpJbnN0YWxsIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCg0KYGBge3J9DQppbnN0YWxsLnBhY2thZ2VzKCJDNTAiKQ0KaW5zdGFsbC5wYWNrYWdlcygicHJpbnRyIikNCmluc3RhbGwucGFja2FnZXMoImNhcmV0IikNCmBgYA0KDQpMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCg0KYGBge3J9DQoNCmxpYnJhcnkoQzUwKQ0KbGlicmFyeShwcmludHIpDQpsaWJyYXJ5KGNhcmV0KQ0KYGBgDQoNClNldCBhIHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQ0KDQpgYGB7cn0NCnNldC5zZWVkKDE5NTgpDQoNCmBgYA0KDQpTcGxpdHRpbmcgdGhlIGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdCBzZXRzDQoNCmBgYHtyfQ0KdHJhaW5faW5kaWNlcyA8LSBzYW1wbGUoMTpucm93KEhvYmJ5X0RhdGEpLCAwLjggKiBucm93KEhvYmJ5X0RhdGEpKQ0KSG9iYnkudHJhaW4gPC0gSG9iYnlfRGF0YVt0cmFpbl9pbmRpY2VzLCBdDQpIb2JieS50ZXN0IDwtIEhvYmJ5X0RhdGFbLXRyYWluX2luZGljZXMsIF0NCg0KYGBgDQoNClRyYWluaW5nIHRoZSBkZWNpc2lvbiB0cmVlIG1vZGVsDQoNCmBgYHtyfQ0KbW9kZWwgPC0gQzUuMChgUHJlZGljdGVkIEhvYmJ5YCB+IC4sIGRhdGEgPSBIb2JieS50cmFpbiwgY29udHJvbCA9IEM1LjBDb250cm9sKENGID0gMC4wMSkpDQoNCmBgYA0KDQpNYWtpbmcgcHJlZGljdGlvbnMgb24gdGhlIHRlc3Qgc2V0DQoNCmBgYHtyfQ0KcHJlZGljdGlvbnMgPC0gcHJlZGljdChtb2RlbCwgbmV3ZGF0YSA9IEhvYmJ5LnRlc3QsIHR5cGUgPSAnY2xhc3MnKQ0KYGBgDQoNCkNyZWF0ZSBhIGNvbmZ1c2lvbiBtYXRyaXggZnJvbSB0aGUgcHJlZGljdGlvbnMgYW5kIGFjdHVhbCB2YWx1ZXMNCg0KYGBge3J9DQpjb25mX21hdHJpeCA8LSB0YWJsZShQcmVkaWN0ZWQgPSBwcmVkaWN0aW9ucywgQWN0dWFsID0gSG9iYnkudGVzdCRgUHJlZGljdGVkIEhvYmJ5YCkNCmBgYA0KDQpDYWxjdWxhdGUgYW5kIHByaW50IHRoZSBhY2N1cmFjeSBvZiB0aGUgbW9kZWwNCg0KYGBge3J9DQphY2N1cmFjeSA8LSBzdW0oZGlhZyhjb25mX21hdHJpeCkpIC8gc3VtKGNvbmZfbWF0cml4KQ0KcHJpbnQocGFzdGUoJ0FjY3VyYWN5IG9uIHRlc3QgZGF0YSBpczonLCBhY2N1cmFjeSkpDQpgYGANCg0KSW5pdGlhbGl6ZSB2ZWN0b3JzIHRvIGhvbGQgdGhlIG1ldHJpY3MgZm9yIGVhY2ggY2xhc3MNCg0KYGBge3J9DQpwcmVjaXNpb24gPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCnJlY2FsbCA8LSBudW1lcmljKGxlbmd0aCA9IG5yb3coY29uZl9tYXRyaXgpKQ0Kc3BlY2lmaWNpdHkgPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCmBgYA0KDQpDYWxjdWxhdGUgbWV0cmljcyBmb3IgZWFjaCBjbGFzcw0KDQpgYGB7cn0NCmZvciAoaSBpbiAxOm5yb3coY29uZl9tYXRyaXgpKSB7DQogIFRQIDwtIGNvbmZfbWF0cml4W2ksIGldDQogIEZQIDwtIHN1bShjb25mX21hdHJpeFssIGldKSAtIFRQDQogIEZOIDwtIHN1bShjb25mX21hdHJpeFtpLCBdKSAtIFRQDQogIFROIDwtIHN1bShjb25mX21hdHJpeCkgLSBUUCAtIEZQIC0gRk4NCiAgDQogIHByZWNpc2lvbltpXSA8LSBUUCAvIChUUCArIEZQKQ0KICByZWNhbGxbaV0gPC0gVFAgLyAoVFAgKyBGTikNCiAgc3BlY2lmaWNpdHlbaV0gPC0gVE4gLyAoVE4gKyBGUCkNCn0NCmBgYA0KDQpBdmVyYWdlIHRoZSBtZXRyaWNzIGlmIHlvdSB3YW50IGEgc2luZ2xlIHBlcmZvcm1hbmNlIG1lYXN1cmUNCg0KYGBge3J9DQphdmdfcHJlY2lzaW9uIDwtIG1lYW4ocHJlY2lzaW9uKQ0KYXZnX3JlY2FsbCA8LSBtZWFuKHJlY2FsbCkNCmF2Z19zcGVjaWZpY2l0eSA8LSBtZWFuKHNwZWNpZmljaXR5KQ0KYGBgDQoNCk91dHB1dCB0aGUgZXZhbHVhdGlvbiBtZXRyaWNzDQoNCmBgYHtyfQ0KcHJpbnQocGFzdGUoIk92ZXJhbGwgQWNjdXJhY3k6IiwgYWNjdXJhY3kpKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgUHJlY2lzaW9uOiIsIGF2Z19wcmVjaXNpb24pKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgUmVjYWxsIChTZW5zaXRpdml0eSk6IiwgYXZnX3JlY2FsbCkpDQpwcmludChwYXN0ZSgiQXZlcmFnZSBTcGVjaWZpY2l0eToiLCBhdmdfc3BlY2lmaWNpdHkpKQ0KYGBgDQoNCnByaW50IHRoZSBtZXRyaWNzIGZvciBlYWNoIGNsYXNzOg0KDQpgYGB7cn0NCg0KbWV0cmljcyA8LSBkYXRhLmZyYW1lKA0KICBDbGFzcyA9IHJvd25hbWVzKGNvbmZfbWF0cml4KSwNCiAgUHJlY2lzaW9uID0gcHJlY2lzaW9uLA0KICBSZWNhbGwgPSByZWNhbGwsDQogIFNwZWNpZmljaXR5ID0gc3BlY2lmaWNpdHkNCikNCmBgYA0KDQpQcmludCBtZXRyaWNzDQoNCmBgYHtyfQ0KcHJpbnQobWV0cmljcykNCmBgYA0KDQpHZW5lcmF0ZSBhbmQgcHJpbnQgYWRkaXRpb25hbCBwZXJmb3JtYW5jZSBtZXRyaWNzIHVzaW5nIGNhcmV0IHBhY2thZ2UNCg0KYGBge3J9DQpjb25mdXNpb25NYXRyaXgocHJlZGljdGlvbnMsIEhvYmJ5LnRlc3QkYFByZWRpY3RlZCBIb2JieWApDQpgYGANCg0KUGxvdCB0aGUgZGVjaXNpb24gdHJlZQ0KDQpgYGB7cn0NCnBsb3QobW9kZWwpDQpgYGANCg0KKipEZWNpc2lvbiBUcmVlIEFuYWx5c2lzIFVzaW5nIEdhaW4gUmF0aW8oODAvMjApOioqDQoNClRoZSBkZWNpc2lvbiB0cmVlIGRlcGljdGVkIGNsYXNzaWZpZXMgaG9iYmllcyBpbnRvICdBY2FkZW1pY3MnICgxKSwgJ0FydHMnICgyKSwgYW5kICdTcG9ydHMnICgzKS4gSXQgc3RhcnRzIHdpdGggJ0NhcmVlcl9zcHJ0JyBhIHZhbHVlIG9mIDAgbGVhZHMgdG8gJ1dvbl9hcnRzJy4gSWYgJ1dvbl9hcnRzJyBpcyAwIG9yIDIsIHRoZSBtb2RlbCBzdWdnZXN0cyAnQWNhZGVtaWNzJyBvciAnQXJ0cycuIElmICdDYXJlZXJfc3BydCcgaXMgMSwgJ0ZhbnRfYXJ0cycgaXMgY29uc2lkZXJlZCBuZXh0OyBhIHZhbHVlIG9mIDAgYWZ0ZXIgJ1dvbl9hcnRzJyBiZWluZyAxIHBvaW50cyB0b3dhcmRzICdBcnRzJywgd2hpbGUgYSB2YWx1ZSBvZiAxIGxlYWRzIHRvICdPbHltcGlhZF9QYXJ0aWNpcGF0aW9uJywgd2hpY2gsIGlmIDEsIGluZGljYXRlcyAnQWNhZGVtaWNzJy4gQ29udmVyc2VseSwgYSBoaWdoICdHcmFzcF9wb3cnIChcPjQpIHByZWRpY3RzICdTcG9ydHMnLg0KDQoNCg0KKiozLUdhaW4gcmF0aW8oNzAlLDMwJSkqKg0KDQpJbnN0YWxsIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCg0KYGBge3J9DQppbnN0YWxsLnBhY2thZ2VzKCJDNTAiKQ0KaW5zdGFsbC5wYWNrYWdlcygicHJpbnRyIikNCmluc3RhbGwucGFja2FnZXMoImNhcmV0IikNCmBgYA0KDQpMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCg0KYGBge3J9DQoNCmxpYnJhcnkoQzUwKQ0KbGlicmFyeShwcmludHIpDQpsaWJyYXJ5KGNhcmV0KQ0KYGBgDQoNClNldCBhIHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQ0KDQpgYGB7cn0NCnNldC5zZWVkKDE5NTgpDQoNCmBgYA0KDQpTcGxpdHRpbmcgdGhlIGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdCBzZXRzDQoNCmBgYHtyfQ0KdHJhaW5faW5kaWNlcyA8LSBzYW1wbGUoMTpucm93KEhvYmJ5X0RhdGEpLCAwLjcgKiBucm93KEhvYmJ5X0RhdGEpKQ0KSG9iYnkudHJhaW4gPC0gSG9iYnlfRGF0YVt0cmFpbl9pbmRpY2VzLCBdDQpIb2JieS50ZXN0IDwtIEhvYmJ5X0RhdGFbLXRyYWluX2luZGljZXMsIF0NCg0KYGBgDQoNClRyYWluaW5nIHRoZSBkZWNpc2lvbiB0cmVlIG1vZGVsDQoNCmBgYHtyfQ0KbW9kZWwgPC0gQzUuMChgUHJlZGljdGVkIEhvYmJ5YCB+IC4sIGRhdGEgPSBIb2JieS50cmFpbiwgY29udHJvbCA9IEM1LjBDb250cm9sKENGID0gMC4wMSkpDQoNCmBgYA0KDQpNYWtpbmcgcHJlZGljdGlvbnMgb24gdGhlIHRlc3Qgc2V0DQoNCmBgYHtyfQ0KcHJlZGljdGlvbnMgPC0gcHJlZGljdChtb2RlbCwgbmV3ZGF0YSA9IEhvYmJ5LnRlc3QsIHR5cGUgPSAnY2xhc3MnKQ0KYGBgDQoNCkNyZWF0ZSBhIGNvbmZ1c2lvbiBtYXRyaXggZnJvbSB0aGUgcHJlZGljdGlvbnMgYW5kIGFjdHVhbCB2YWx1ZXMNCg0KYGBge3J9DQpjb25mX21hdHJpeCA8LSB0YWJsZShQcmVkaWN0ZWQgPSBwcmVkaWN0aW9ucywgQWN0dWFsID0gSG9iYnkudGVzdCRgUHJlZGljdGVkIEhvYmJ5YCkNCmBgYA0KDQpDYWxjdWxhdGUgYW5kIHByaW50IHRoZSBhY2N1cmFjeSBvZiB0aGUgbW9kZWwNCg0KYGBge3J9DQphY2N1cmFjeSA8LSBzdW0oZGlhZyhjb25mX21hdHJpeCkpIC8gc3VtKGNvbmZfbWF0cml4KQ0KcHJpbnQocGFzdGUoJ0FjY3VyYWN5IG9uIHRlc3QgZGF0YSBpczonLCBhY2N1cmFjeSkpDQpgYGANCg0KSW5pdGlhbGl6ZSB2ZWN0b3JzIHRvIGhvbGQgdGhlIG1ldHJpY3MgZm9yIGVhY2ggY2xhc3MNCg0KYGBge3J9DQpwcmVjaXNpb24gPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCnJlY2FsbCA8LSBudW1lcmljKGxlbmd0aCA9IG5yb3coY29uZl9tYXRyaXgpKQ0Kc3BlY2lmaWNpdHkgPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCmBgYA0KDQpDYWxjdWxhdGUgbWV0cmljcyBmb3IgZWFjaCBjbGFzcw0KDQpgYGB7cn0NCmZvciAoaSBpbiAxOm5yb3coY29uZl9tYXRyaXgpKSB7DQogIFRQIDwtIGNvbmZfbWF0cml4W2ksIGldDQogIEZQIDwtIHN1bShjb25mX21hdHJpeFssIGldKSAtIFRQDQogIEZOIDwtIHN1bShjb25mX21hdHJpeFtpLCBdKSAtIFRQDQogIFROIDwtIHN1bShjb25mX21hdHJpeCkgLSBUUCAtIEZQIC0gRk4NCiAgDQogIHByZWNpc2lvbltpXSA8LSBUUCAvIChUUCArIEZQKQ0KICByZWNhbGxbaV0gPC0gVFAgLyAoVFAgKyBGTikNCiAgc3BlY2lmaWNpdHlbaV0gPC0gVE4gLyAoVE4gKyBGUCkNCn0NCmBgYA0KDQpBdmVyYWdlIHRoZSBtZXRyaWNzIGlmIHlvdSB3YW50IGEgc2luZ2xlIHBlcmZvcm1hbmNlIG1lYXN1cmUNCg0KYGBge3J9DQphdmdfcHJlY2lzaW9uIDwtIG1lYW4ocHJlY2lzaW9uKQ0KYXZnX3JlY2FsbCA8LSBtZWFuKHJlY2FsbCkNCmF2Z19zcGVjaWZpY2l0eSA8LSBtZWFuKHNwZWNpZmljaXR5KQ0KYGBgDQoNCk91dHB1dCB0aGUgZXZhbHVhdGlvbiBtZXRyaWNzDQoNCmBgYHtyfQ0KcHJpbnQocGFzdGUoIk92ZXJhbGwgQWNjdXJhY3k6IiwgYWNjdXJhY3kpKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgUHJlY2lzaW9uOiIsIGF2Z19wcmVjaXNpb24pKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgUmVjYWxsIChTZW5zaXRpdml0eSk6IiwgYXZnX3JlY2FsbCkpDQpwcmludChwYXN0ZSgiQXZlcmFnZSBTcGVjaWZpY2l0eToiLCBhdmdfc3BlY2lmaWNpdHkpKQ0KYGBgDQoNCnByaW50IHRoZSBtZXRyaWNzIGZvciBlYWNoIGNsYXNzOg0KDQpgYGB7cn0NCg0KbWV0cmljcyA8LSBkYXRhLmZyYW1lKA0KICBDbGFzcyA9IHJvd25hbWVzKGNvbmZfbWF0cml4KSwNCiAgUHJlY2lzaW9uID0gcHJlY2lzaW9uLA0KICBSZWNhbGwgPSByZWNhbGwsDQogIFNwZWNpZmljaXR5ID0gc3BlY2lmaWNpdHkNCikNCmBgYA0KDQpQcmludCBtZXRyaWNzDQoNCmBgYHtyfQ0KcHJpbnQobWV0cmljcykNCmBgYA0KDQpHZW5lcmF0ZSBhbmQgcHJpbnQgYWRkaXRpb25hbCBwZXJmb3JtYW5jZSBtZXRyaWNzIHVzaW5nIGNhcmV0IHBhY2thZ2UNCg0KYGBge3J9DQpjb25mdXNpb25NYXRyaXgocHJlZGljdGlvbnMsIEhvYmJ5LnRlc3QkYFByZWRpY3RlZCBIb2JieWApDQpgYGANCg0KUGxvdCB0aGUgZGVjaXNpb24gdHJlZQ0KDQpgYGB7cn0NCnBsb3QobW9kZWwpDQpgYGANCg0KKipEZWNpc2lvbiBUcmVlIEFuYWx5c2lzIFVzaW5nIEdhaW4gUmF0aW8oNzAlLzMwJSk6KioNCg0KSW4gVGhpcmQgVHJlZSAsd2UgZGV2aWRlIGRhdGFzZXQgaW50byB0cmFpbmluZyBzZXQgYW5kIHRlc3Qgc2V0IHdpdGggc2l6ZSglNzAsJTMwKSByZXNwZWN0aXZlbHkuIEFzIHlvdSBjYW4gc2VlIGluIHRoZSBmaWd1cmUsIHRoZSByb290IG5vZGUgaXMgIkNhcmVlcl9zcHJ0IiAsIGNsYXNzIDEoIkFjYWRlbWljcyIpLCBjbGFzcyAyKCJBcnRzIiksIGFuZCBjbGFzcyAzICgiU3BvcnRzIikuDQoNClRoZSBmaXJzdCBkZWNpc2lvbiBpcyBiYXNlZCBvbiB3aGV0aGVyIHRoZSB2YWx1ZSBvZiB0aGUgIkNhcmVlcl9zcHJ0IiBhdHRyaWJ1dGUgaXMgMC5JZiAiQ2FyZWVyX3NwcnQiIGlzIDAsIHRoZW4gY2hlY2sgaWYgIldvbl9hcnRzIiBpcyBlaXRoZXIgMCBvciAyLnByZWRpY3QgIkFjYWRlbWljcyIuSWYgIldvbl9hcnRzIiBpcyAxLCB0aGVuIGNoZWNrIGlmICJGYW50X2FydHMiIGlzIDEscHJlZGljdCAiQXJ0cyIuSWYgIiJGYW50X2FydHMgaXMgMCBhbmQiV29uX2FydHMiIGlzIDAgb3IgMiwgdGhlbiBjaGVjayBpZiAiVGltZV9hcnQiIGlzIGxlc3MgdGhhbiBvciBlcXVhbCB0byAyLHByZWRpY3QgIkFjYWRlbWljcyIuIElmICJUaW1lX2FydCIgaXMgZ3JlYXRlciB0aGFuIDIsIHByZWRpY3QgIkFydHMiLg0KDQpJZiAiQ2FyZWVyX3NwcnQiaXMgMSwgdGhlbiBjaGVjayBpZiAiRmFudF9hcnRzIiBpcyAwLCBwcmVkaWN0ICJTcG9ydHMiLklmICJGYW50X2FydHMiaXMgMSwgdGhlbiBjaGVjayBpZiAiVGltZV9hcnQiIGlzIGxlc3MgdGhhbiBvciBlcXVhbCB0byAyIGNoZWNrIGlmICJBY3Rfc3BydCIgaXMgMCxwcmVkaWN0ICJBY2FkZW1pY3MiIC5JZiAiQWN0X3NwcnQiIGlzIDEgcHJlZGljdCAiU3BvcnRzIi4gaWYgIlRpbWVfYXJ0IiBpcyBncmVhdGVyIHRoYW4gMiwgdGhlbiBjaGVjayBpZiAiV29uX2FydHMiIGlzIDAgcHJlZGljdCAiU3BvcnRzIi5JZiAiV29uX2FydHMiIGlzIDEgb3IgMiBwcmVkaWN0ICJBcnRzIi4NCg0KKipDb21wYXJpbmcgRGVjaXNpb24gVHJlZSBSZXN1bHRzIFVzaW5nIEdhaW4gUmF0aW8qKg0KDQpBY3Jvc3MgVGhyZWUgVHJhaW5pbmctVGVzdCBTaXplczogVGhlIGFjY3VyYWN5IHJhdGVzIC0wLjg5NDQgZm9yIHRoZSA5MDoxMCBzcGxpdCwgMC44OTM5IGZvciB0aGUgNzA6MzAgc3BsaXQsIGFuZCAwLjg4NzkgZm9yIHRoZSA4MDoyMCBzcGxpdCAtLSBpbmRpY2F0ZSBvbmx5IHNsaWdodCB2YXJpYXRpb25zLCB3aXRoIHRoZSA5MDoxMCBzcGxpdCBiZWluZyBtYXJnaW5hbGx5IGJldHRlci4NCg0KDQorLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IEdhaW4gcmF0aW8gICAgICB8IDkwICV0IHJhaW5pbmcgc2V0IHwgODAgJXQgcmFpbmluZyBzZXQgfCA3MCAldCByYWluaW5nIHNldCB8DQp8ICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICB8IDEwJSB0ZXN0aW5nIHNldDogIHwgMjAlIHRlc3Rpbmcgc2V0OiAgfCAzMCUgdGVzdGluZyBzZXQ6ICB8DQorOj09PT09PT09PT09PT09PTorOj09PT09PT09PT09PT09PT09Ois6PT09PT09PT09PT09PT09PT06Kzo9PT09PT09PT09PT09PT09PTorDQp8ICoqQWNjdXJhY3kqKiAgICB8IDAuODk0NCAgICAgICAgICAgIHwgMC44ODggICAgICAgICAgICAgfCAwLjg5Mzk3MDg5Mzk3MDg5NCB8DQorLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICoqcHJlY2lzaW9uKiogICB8IDAuODkyICAgICAgICAgICAgIHwgMC44ODEgICAgICAgICAgICAgfCAwLjg5MCAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICoqc2Vuc2l0aXZpdHkqKiB8IDAuODkxICAgICAgICAgICAgIHwgMC44OTAgICAgICAgICAgICAgfCAwLjkwMiAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICoqc3BlY2lmaWNpdHkqKiB8IDAuOTQ1ICAgICAgICAgICAgIHwgMC45NDMgICAgICAgICAgICAgfCAwLjk0NyAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0rDQoNCg0KDQoNCg0KDQoNCg0KDQoqKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1DbHVzdGVyaW5nLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSoqDQoNCg0KDQpgYGB7cn0NCnN0cihIb2JieV9wcm9jKQ0KYGBgDQoNCioqVG8gY29udmVydCB0eXBlIGZyb20gZmFjdG9yIGNvbHVtbnMgdG8gbnVtZXJpYyoqDQoNCmBgYHtyfQ0KDQpIb2JieV9wcm9jJEZhdl9zdWIgPC1hcy5udW1lcmljKEhvYmJ5X3Byb2MkRmF2X3N1YikNCg0KSG9iYnlfcHJvYyRPbHltcGlhZF9QYXJ0aWNpcGF0aW9uIDwtYXMubnVtZXJpYyhIb2JieV9wcm9jJE9seW1waWFkX1BhcnRpY2lwYXRpb24pDQoNCkhvYmJ5X3Byb2MkUHJvamVjdHMgPC1hcy5udW1lcmljKEhvYmJ5X3Byb2MkUHJvamVjdHMpDQoNCkhvYmJ5X3Byb2MkU2Nob2xhcnNoaXAgPC1hcy5udW1lcmljKEhvYmJ5X3Byb2MkU2Nob2xhcnNoaXApDQoNCkhvYmJ5X3Byb2MkQ2FyZWVyX3NwcnQgPC1hcy5udW1lcmljKEhvYmJ5X3Byb2MkQ2FyZWVyX3NwcnQpDQoNCkhvYmJ5X3Byb2MkQWN0X3NwcnQgPC1hcy5udW1lcmljKEhvYmJ5X3Byb2MkQWN0X3NwcnQpDQoNCkhvYmJ5X3Byb2MkRmFudF9hcnRzIDwtYXMubnVtZXJpYyhIb2JieV9wcm9jJEZhbnRfYXJ0cykNCg0KSG9iYnlfcHJvYyRXb25fYXJ0cyA8LWFzLm51bWVyaWMoSG9iYnlfcHJvYyRXb25fYXJ0cykNCg0KSG9iYnlfcHJvYyRgUHJlZGljdGVkIEhvYmJ5YCA8LWFzLm51bWVyaWMoSG9iYnlfcHJvYyRgUHJlZGljdGVkIEhvYmJ5YCApDQpgYGANCg0KDQpgYGB7cn0NCnN0cihIb2JieV9wcm9jKQ0KYGBgDQoNCioqU2NhbGVkIEFsbCBDb2x1bW5zKioNCg0KYGBge3J9DQpIb2JieV9wcm9jIDwtc2NhbGUoSG9iYnlfcHJvYykNCmBgYA0KDQoqKkRhdGEgc2V0IHdpdGhvdXQgZ3JvdW5kIHRydXRoKioNCg0KYGBge3J9DQpIb2JieV9EYXRhMiA8LSBIb2JieV9wcm9jWywgIShjb2xuYW1lcyhIb2JieV9wcm9jKSAlaW4lIGMoIlByZWRpY3QgSG9iYnkiKSldDQpgYGANCg0KDQpPdXIgZGF0YSBzZXQgZXhoaWJpdHMgYSBiYWxhbmNlZCBkaXN0cmlidXRpb24gYW1vbmcgY2xhc3MgbGFiZWxzLCB3aXRoDQoiYWNhZGVtaWMsIiAiYXJ0LCIgYW5kICJzcG9ydCIgY29uc3RpdHV0aW5nIDQzLjYlLCAyNS42JSwgYW5kIDMwLjglIG9mDQp0aGUgdG90YWwsIHJlc3BlY3RpdmVseS4gVGhpcyBiYWxhbmNlZCBkaXN0cmlidXRpb24gaXMgYWR2YW50YWdlb3VzIGZvcg0KYm90aCBjbGFzc2lmaWNhdGlvbiBhbmQgY2x1c3RlcmluZyB0YXNrcy4gSW4gY2xhc3NpZmljYXRpb24sIGEgYmFsYW5jZWQNCmRhdGEgc2V0IGhlbHBzIHByZXZlbnQgdGhlIG1vZGVsIGZyb20gZmF2b3Jpbmcgb25lIGNsYXNzIG92ZXIgdGhlDQpvdGhlcnMsIGVuc3VyaW5nIHRoYXQgdGhlIGxlYXJuaW5nIGFsZ29yaXRobSBpcyBleHBvc2VkIHRvIGENCnJlcHJlc2VudGF0aXZlIHNldCBvZiBleGFtcGxlcyBmcm9tIGVhY2ggY2F0ZWdvcnkuIFRoaXMgYmFsYW5jZSBwcm9tb3Rlcw0KdGhlIGRldmVsb3BtZW50IG9mIGEgbW9kZWwgdGhhdCBnZW5lcmFsaXplcyB3ZWxsIGFjcm9zcyBhbGwgY2xhc3NlcywNCmVuaGFuY2luZyBpdHMgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZSBvbiBuZXcsIHVuc2VlbiBkYXRhLiBJbiBjbHVzdGVyaW5nLA0KYSBiYWxhbmNlZCBkYXRhIHNldCBhaWRzIGluIGZvcm1pbmcgY2x1c3RlcnMgdGhhdCBhcmUgbW9yZSBldmVubHkNCmRpc3RyaWJ1dGVkLCBhbGxvd2luZyBmb3IgYSBjb21wcmVoZW5zaXZlIHVuZGVyc3RhbmRpbmcgb2YgcGF0dGVybnMgYW5kDQpyZWxhdGlvbnNoaXBzIGFjcm9zcyBkaXZlcnNlIGNhdGVnb3JpZXMuIEJhbGFuY2VkIGRhdGEgc2V0cyBvZnRlbiBsZWFkDQp0byBtb3JlIGFjY3VyYXRlIGFuZCBmYWlyIGNsdXN0ZXJpbmcgcmVzdWx0cywgZW5hYmxpbmcgbWVhbmluZ2Z1bA0KaW5zaWdodHMgaW50byB0aGUgdW5kZXJseWluZyBzdHJ1Y3R1cmVzIHdpdGhpbiBlYWNoIGNsYXNzLg0KDQoqKnJlcXVpcmUgcGFja2FnZXMvTGlicmFyeSoqDQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpIA0KaW5zdGFsbC5wYWNrYWdlcygibWFncml0dHIiKQ0KaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQ0KbGlicmFyeShmYWN0b2V4dHJhKSANCmxpYnJhcnkoY2x1c3RlcikNCmxpYnJhcnkoZHBseXIpDQpgYGANCg0KDQoNCioqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tay1tZWFuc2NsdXN0ZXJpbmctLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qKg0KDQoqKlZhbGlkYXRpb246KioNCg0KRGV0ZXJtaW5pbmcgdGhlIHJpZ2h0IG51bWJlciBvZiBjbHVzdGVycyBiZWZvcmUgc3RhcnRpbmcgdGhlIGNsdXN0ZXJpbmcNCnByb2Nlc3MgaXMgbGlrZSBtYWtpbmcgc3VyZSB5b3UgaGF2ZSB0aGUgY29ycmVjdC1zaXplZCBwdXp6bGUgcGllY2VzDQpiZWZvcmUgcHV0dGluZyB0aGUgcHV6emxlIHRvZ2V0aGVyLiBJdCdzIGltcG9ydGFudCBiZWNhdXNlIHNvbWUNCmNsdXN0ZXJpbmcgYWxnb3JpdGhtcywgbGlrZSBLLW1lYW5zLCByZXF1aXJlIHN1Y2ggYSBwYXJhbWV0ZXIuIEluDQphZGRpdGlvbiB0byB0aGF0LCBpdCBoZWxwcyBtYWtlIHRoZSBjbHVzdGVyaW5nIG1vcmUgYWNjdXJhdGUgYW5kIHVzZWZ1bC4NCklmIHlvdSBrbm93IHRoZSByaWdodCBudW1iZXIgYmVmb3JlaGFuZCwgaXQgc2F2ZXMgdGltZSBhbmQgaGVscHMgbWFrZQ0KdGhlIHdob2xlIHByb2Nlc3MgbW9yZSBlZmZpY2llbnQgYW5kIHRoZSByZXN1bHRzIG1vcmUgcmVsaWFibGUuDQoNCioqY29tcHV0ZSBhdmVyYWdlIHNpbGhvdWV0dGUgZm9yIGsgY2x1c3RlcnMgdXNpbmcgc2lsaG91ZXR0ZSgpIEZvciBrLW1lYW4qKg0KDQpgYGB7cn0NCnNpbGhvdWV0dGVfc2NvcmUgPC0gZnVuY3Rpb24oaykgew0KICBrbSA8LSBrbWVhbnMoSG9iYnlfRGF0YTIsIGNlbnRlcnMgPSBrLCBuc3RhcnQgPSAyNSkNCiAgc3MgPC0gc2lsaG91ZXR0ZShrbSRjbHVzdGVyLCBkaXN0KEhvYmJ5X0RhdGEyKSkNCiAgc2lsIDwtIG1lYW4oc3NbLCAzXSkNCiAgcmV0dXJuKHNpbCkNCn0NCg0KIyBrIGNsdXN0ZXIgcmFuZ2UgZnJvbSAyIHRvIDEwDQprIDwtIDI6MTANCg0KIyBjYWxsIGZ1bmN0aW9uIGZvciBlYWNoIGsgdmFsdWUNCmF2Z19zaWwgPC0gc2FwcGx5KGssIHNpbGhvdWV0dGVfc2NvcmUpDQoNCiMgcGxvdCB0aGUgcmVzdWx0cw0KcGxvdChrLCBhdmdfc2lsLCB0eXBlID0gJ2InLCB4bGFiID0gJ051bWJlciBvZiBjbHVzdGVycycsIHlsYWIgPSAnQXZlcmFnZSBTaWxob3VldHRlIFNjb3JlcycsIGZyYW1lID0gRkFMU0UpDQpgYGANCg0KVGhlIHJlc3VsdHMgaW5kaWNhdGUgdGhhdCB0aGUgaGlnaGVzdCBhdmVyYWdlIHNpbGhvdWV0dGUgc2NvcmVzIGluZGljYXRlDQp0aGUgcXVhbGl0eSBvZiB0aGUgY2x1c3RlcnMgYW5kIHN1Z2dlc3QgYmV0dGVyLWRlZmluZWQgYW5kIG1vcmUNCnNlcGFyYXRlZCBjbHVzdGVycywgd2l0aCBlYWNoIHBvaW50IGhhdmluZyBhIGhpZ2ggZGVncmVlIG9mIHNpbWlsYXJpdHkNCnRvIGl0cyBvd24gY2x1c3RlciBhbmQgYSBsb3dlciBzaW1pbGFyaXR5IHRvIG5laWdoYm9yaW5nIGNsdXN0ZXJzLiBUaGUNCmhpZ2hlc3QgYXZlcmFnZSBzaWxob3VldHRlIHNjb3JlcyB3ZXJlIG9ic2VydmVkIGZvciBrIHZhbHVlcyBvZiAzLCAyLA0KYW5kIDQsIHNvIHRoZXNlIGFyZSB0aGUgb3B0aW1hbCBudW1iZXJzLiBUaGVzZSB2YWx1ZXMgd2lsbCBiZSBlbXBsb3llZA0KaW4gc3Vic2VxdWVudCBrLW1lYW5zIGNsdXN0ZXJpbmcgYW5hbHlzZXMuDQoNCioqay1tZWFucyBjbHVzdGVyIGs9MyoqDQoNCmBgYHtyfQ0KI3NldCBhIHNlZWQgZm9yIHJhbmRvbSBudW1iZXIgZ2VuZXJhdGlvbiAgdG8gbWFrZSB0aGUgcmVzdWx0cyByZXByb2R1Y2libGUNCnNldC5zZWVkKDcpDQprbWVhbnMucmVzdWx0IDwtIGttZWFucyhIb2JieV9EYXRhMiwgMykNCg0KIyBwcmludCB0aGUgY2x1c3Rlcm5nIHJlc3VsdA0Ka21lYW5zLnJlc3VsdA0KDQojdmlzdWFsaXplIGNsdXN0ZXJpbmcNCmZ2aXpfY2x1c3RlcihrbWVhbnMucmVzdWx0LCBkYXRhID0gSG9iYnlfRGF0YTIpDQpgYGANCg0KKipBdmVyYWdlIGZvciBlYWNoIGNsdXN0ZXIqKg0KDQpgYGB7cn0NCmF2Z19zaWwgPC0gc2lsaG91ZXR0ZShrbWVhbnMucmVzdWx0JGNsdXN0ZXIsZGlzdChIb2JieV9EYXRhMikpIA0KZnZpel9zaWxob3VldHRlKGF2Z19zaWwpDQpgYGANCg0KdGhlIHByZXNlbmNlIG9mIG5lZ2F0aXZlIHNpbGhvdWV0dGVzIGZvciBzb21lIG9ic2VydmF0aW9ucyB3aXRoaW4gZWFjaA0KY2x1c3RlciBpbmRpY2F0ZXMgdGhhdCB0aGVzZSBwb2ludHMgbWlnaHQgYmUgbW9yZSBzaW1pbGFyIHRvIHBvaW50cyBpbg0Kb3RoZXIgY2x1c3RlcnMsIHN1Z2dlc3RpbmcgYSBwb3RlbnRpYWwgb3ZlcmxhcCBvciBhbWJpZ3VpdHkgaW4gdGhlaXINCmFzc2lnbm1lbnQuIFRoZSBmYWN0IHRoYXQgc29tZSBvYnNlcnZhdGlvbnMgaGF2ZSBuZWdhdGl2ZSBzaWxob3VldHRlcw0KaGlnaGxpZ2h0cyB0aGF0IHRoZSBzZXBhcmF0aW9uIG9mIGRhdGEgcG9pbnRzIGlzIG5vdCBlbnRpcmVseQ0Kc3VmZmljaWVudC4NCg0KU28gc2luY2UgMywgd2hpY2ggd2FzIHRoZSBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycyB3aXRoIHRoZSBoaWdoZXN0DQpzaWxob3VldHRlIHNjb3JlIGF2ZXJhZ2UsIGRpZCBub3QgaGF2ZSBnb29kIGNsdXN0ZXJpbmcgcmVzdWx0cywgdGhhdA0KZG9lcyBzdXBwb3J0IG91ciByZXNlYXJjaCByZXN1bHRzIHRoYXQgdGhlIGstbWVhbnMgYWxnb3JpdGhtIGlzIG5vdA0KYXBwbGljYWJsZSB0byBjYXRlZ29yaWNhbGRhdGEgY2x1c3RlcmluZyBiZWNhdXNlIGl0IHJlbGllcyBvbiB0aGUNCkV1Y2xpZGVhbiBkaXN0YW5jZSBtZXRyaWMgdG9tZWFzdXJlIHRoZSBzaW1pbGFyaXR5IGJldHdlZW4gZGF0YSBwb2ludHMuDQpIb3dldmVyLCBldmVuIGFmdGVyRW5jb2RpbmcgYW5kIGl0cyBhcHBsaWNhdGlvbiB0byBjYXRlZ29yaWNhbCBkYXRhIHBvc2UNCnNpZ25pZmljYW50IGNoYWxsZW5nZXMuIENhdGVnb3JpY2FsIHZhcmlhYmxlcyBvZnRlbiBsYWNrIGEgbWVhbmluZ2Z1bA0KbnVtZXJpY2FscmVwcmVzZW50YXRpb247IGZvciBpbnN0YW5jZSwgdGFraW5nIHRoZSBtZWFuIG9mIGNhdGVnb3JpZXMNCmxpa2UgdGhlZmVhdHVyZSAiZmF2b3JpdGUgc3ViamVjdCwiIGFrYSBGYXZfc3ViIiAoZXZlbiBhZnRlciBlbmNvZGluZyksDQptaWdodCBub3RoYXZlIGFueSBwcmFjdGljYWwgaW50ZXJwcmV0YXRpb24uIEFuZCB0aGUgZGlzdGFuY2VzIGNhbGN1bGF0ZWQNCmluIHRoZWFsZ29yaXRobSBtYXkgbm90IHJlZmxlY3QgdGhlIHRydWUgZGlzc2ltaWxhcml0aWVzIGJldHdlZW4NCmNhdGVnb3JpY2FsdmFsdWVzLiBUaGUgZW5jb2RpbmcgcHJvY2VzcyBpdHNlbGYgaW50cm9kdWNlcyBhcnRpZmljaWFsDQpudW1lcmljYWxyZWxhdGlvbnNoaXBzIHRoYXQgbWF5IG1pc2xlYWQgdGhlIGFsZ29yaXRobS4gTW9yZW92ZXIsIGstbWVhbnMNCnJlbGllc29uIHRoZSBtaW5pbWl6YXRpb24gb2YgRXVjbGlkZWFuIGRpc3RhbmNlcywgd2hpY2ggbWlnaHQgbm90DQphY2N1cmF0ZWx5Y2FwdHVyZSB0aGUgZGlzc2ltaWxhcml0eSBzdHJ1Y3R1cmUgaW4gY2F0ZWdvcmljYWwgZGF0YS4NCkNhdGVnb3JpY2FsdmFyaWFibGVzIGluaGVyZW50bHkgZXhoaWJpdCBkaXNjcmV0ZSBhbmQgbm9uLW9yZGluYWwNCmNoYXJhY3RlcmlzdGljc3RoYXQgYXJlIG5vdCB3ZWxsLXN1aXRlZCBmb3IgdGhlIGNvbnRpbnVvdXMgYW5kIGxpbmVhcg0KYXNzdW1wdGlvbnMgb2ZrLW1lYW5zLiBBbHRlcm5hdGl2ZSBjbHVzdGVyaW5nIHRlY2huaXF1ZXMsIHNwZWNpZmljYWxseQ0KZGVzaWduZWQgZm9yY2F0ZWdvcmljYWwgZGF0YSwgc3VjaCBhcyBwYXJ0aXRpb25pbmcgYXJvdW5kIG1lZG9pZHMsIGFyZQ0KbW9yZWFwcHJvcHJpYXRlIGZvciBjYXB0dXJpbmcgdGhlIGludHJpbnNpYyBwYXR0ZXJucyBhbmQgcmVsYXRpb25zaGlwcw0KaW5jYXRlZ29yaWNhbCBkYXRhc2V0cy4NCg0KDQoNCg0KDQoNCg0KKiotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLWstbWVkaW9kcyBjbHVzdGVyaW5nIHdpdGggUEFNLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qKg0KDQpLLW1lZG9pZHMgY2x1c3RlcmluZyBwcmVzZW50cyBhIHJvYnVzdCBhbHRlcm5hdGl2ZSBmb3IgYW5hbHl6aW5nDQpjYXRlZ29yaWNhbCBkYXRhIGJ5IGFkZHJlc3NpbmcgdGhlIGxpbWl0YXRpb25zIHBvc2VkIGJ5IGstbWVhbnMNCmNsdXN0ZXJpbmcuIFVubGlrZSBrLW1lYW5zLCBrLW1lZG9pZHMgZG9lcyBub3QgcmVseSBvbiB0aGUgbWVhbiBhcyBhDQpyZXByZXNlbnRhdGl2ZSBjZW50cm9pZCBidXQgZW1wbG95cyBtZWRvaWRzLCB3aGljaCBhcmUgYWN0dWFsIGRhdGENCnBvaW50cyB3aXRoaW4gdGhlIGNsdXN0ZXJzLCBhbmQgdGhlIGFsZ29yaXRobSBkZWZpbmVzIGNsdXN0ZXJzIGJhc2VkIG9uDQpwYXJ0aXRpb25pbmcgYXJvdW5kIG1lZG9pZHMuIFRoaXMgZmVhdHVyZSBtYWtlcyBrLW1lZG9pZHMgcGFydGljdWxhcmx5DQpzdWl0YWJsZSBmb3IgY2F0ZWdvcmljYWwgZGF0YSwgd2hlcmUgbWVhbmluZ2Z1bCBjZW50cm9pZHMgbWF5IG5vdCBoYXZlIGENCm51bWVyaWNhbCBpbnRlcnByZXRhdGlvbi4gVGhlIGFsZ29yaXRobSBkZWZpbmVzIGNsdXN0ZXJzIGJhc2VkIG9uDQpwYXJ0aXRpb25pbmcgYXJvdW5kIG1lZG9pZHMuDQoNCioqVmFsaWRhdGlvbjoqKg0KDQpGaXJzdCwgd2Ugd2FudCB0byBkZXRlcm1pbmUgdGhyZWUgZGlmZmVyZW50IG51bWJlcnMgb2YgY2x1c3RlcnMgYnkgdXNpbmcNCmEgbnVtYmVyIG9mIG1ldGhvZHMgdGhhdCB3aWxsIHN1Z2dlc3QgdGhlIG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXJzIGZvcg0Kay1tZWRpb2RzIGNsdXN0ZXJpbmcgd2l0aCBQQU0uDQoNCioqU2lsaG91ZXR0ZSBjb2VmZmljaWVudCoqDQoNCmBgYHtyfQ0KZnZpel9uYmNsdXN0KEhvYmJ5X0RhdGEyLCBwYW0sIG1ldGhvZCA9ICJzaWxob3VldHRlIikrDQogIGxhYnMoc3VidGl0bGUgPSAiU2lsaG91ZXR0ZSBtZXRob2QiKQ0KYGBgDQoNCioqRWxib3cgbWV0aG9kKioNCg0KYGBge3J9DQpmdml6X25iY2x1c3QoSG9iYnlfRGF0YTIsIHBhbSwgbWV0aG9kID0gIndzcyIpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PSAzLCBsaW5ldHlwZT0gMykrDQogIGxhYnMoc3VidGl0bGUgPSAiRWxib3cgbWV0aG9kIikNCmBgYA0KDQpUaGUgc2lsaG91ZXR0ZSBjb2VmZmljaWVudCwgd2hpY2ggbWVhc3VyZXMgdGhlIGNvaGVzaW9uIGFuZCBzZXBhcmF0aW9uDQpvZiBjbHVzdGVycywgYWxpZ25zIHdpdGggdGhlIEVsYm93IG1ldGhvZCwgd2hpY2ggYXNzZXNzZXMgdGhlDQp3aXRoaW4tY2x1c3RlciBzdW0gb2Ygc3F1YXJlcyAod3NzKSBhcyBhIGZ1bmN0aW9uIG9mIHRoZSBudW1iZXIgb2YNCmNsdXN0ZXJzIGluIHRoZSBzdWdnZXN0ZWQgb3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlcnMuIFRoaXMgYWxpZ25tZW50IGluDQpyZXN1bHRzIGJldHdlZW4gdHdvIGRpc3RpbmN0IGV2YWx1YXRpb24gbWV0aG9kcyBzdHJlbmd0aGVucyBjb25maWRlbmNlDQppbiB0aGUgY2hvaWNlIG9mIHRocmVlIGNsdXN0ZXJzLCBwcm92aWRpbmcgYSBzdGFibGUgZm91bmRhdGlvbiBmb3INCmZ1cnRoZXIgYW5hbHlzaXMgYW5kIGludGVycHJldGF0aW9uIG9mIHRoZSB1bmRlcmx5aW5nIHBhdHRlcm5zIHdpdGhpbg0KdGhlIGRhdGFzZXQuIEluIGFkZGl0aW9uIHRvIHRoYXQsIHRoZSBncm91bmQgdHJ1dGggKGNsYXNzIGxhYmVscykgYWxzbw0KY29udGFpbnMgdGhyZWUgY2xhc3NlcywgYW5kIHRoYXQgaW5kaWNhdGVzIGEgcmVhc3N1cmluZyBhbGlnbm1lbnQNCmJldHdlZW4gdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YSBhbmQgdGhlIGNsdXN0ZXJpbmcgcmVzdWx0cy4NCg0KDQoNCldlIG5lZWQgdG8gZGV0ZXJtaW5lIHR3byBtb3JlIHN1Z2dlc3RlZCBudW1iZXJzIG9mIGNsdXN0ZXJzIGJ5IGNvbXB1dGluZw0KdGhlIGF2ZXJhZ2Ugc2lsaG91ZXR0ZSBmb3IgayBjbHVzdGVycyB1c2luZyBzaWxob3VldHRlKCkuDQpgYGB7cn0NCnNpbGhvdWV0dGVfc2NvcmUgPC0gZnVuY3Rpb24oaykgew0KICBrbSA8LSBwYW0oSG9iYnlfRGF0YTIsIGssIGRpc3MgPSBUUlVFKQ0KICBzcyA8LSBzaWxob3VldHRlKGttJGNsdXN0ZXJpbmcsIGRpc3QoSG9iYnlfRGF0YTIpKQ0KICBzaWwgPC0gbWVhbihzc1ssIDNdKQ0KICByZXR1cm4oc2lsKQ0KfQ0KDQojIGsgY2x1c3RlciByYW5nZSBmcm9tIDIgdG8gMTANCmsgPC0gMjoxMA0KDQojIENhbGwgZnVuY3Rpb24gZm9yIGVhY2ggayB2YWx1ZQ0KYXZnX3NpbCA8LSBzYXBwbHkoaywgc2lsaG91ZXR0ZV9zY29yZSkNCg0KIyBQbG90IHRoZSByZXN1bHRzDQpwbG90KGssIGF2Z19zaWwsIHR5cGUgPSAnYicsIHhsYWIgPSAnTnVtYmVyIG9mIGNsdXN0ZXJzJywgeWxhYiA9ICdBdmVyYWdlIFNpbGhvdWV0dGUgU2NvcmVzJywgZnJhbWUgPSBGQUxTRSkNCmBgYA0KDQpJdCBpcyBhIGNvbW1vbiBwcmFjdGljZSB0byBjaG9vc2UgdGhlIG51bWJlciBvZiBjbHVzdGVycyBjb3JyZXNwb25kaW5nDQp0byB0aGUgcGVhayBpbiB0aGUgc2lsaG91ZXR0ZSBzY29yZSBwbG90LCBhbmQgc2luY2Ugd2UgYXJlIGxvb2tpbmcgZm9yDQp0d28gbW9yZSBudW1iZXIgb2YgY2x1c3RlcnMgb3RoZXIgdGhhbiB0aHJlZSwgaXQgd291bGQgYmUgcmVhc29uYWJsZSB0bw0KY29uc2lkZXIgdHdvIGFuZCBmb3VyIGNsdXN0ZXJzIGZvciBmdXJ0aGVyIGFuYWx5c2lzLiBFc3BlY2lhbGx5IHdpdGggdGhlDQpkZWNyZWFzaW5nIHRyZW5kIGJleW9uZCB0aHJlZSBjbHVzdGVycywgaXQgaW5kaWNhdGVzIHRoYXQgYWRkaW5nIG1vcmUNCmNsdXN0ZXJzIGRvZXMgbm90IHNpZ25pZmljYW50bHkgaW1wcm92ZSB0aGUgc2VwYXJhdGlvbiBhbmQgY29oZXNpb24gb2YNCnRoZSBjbHVzdGVycy4NCg0KDQoNCioqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1ncm91cCBpbnRvIGs9MyBjbHVzdGVycy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKioNCg0KVGhlIHN1Yi1zYW1wbGluZyBhbmQgY2x1c3RlcmluZyBhcHByb2FjaCBpcyBhIGhlbHBmdWwgbWV0aG9kIHRvIGV2YWx1YXRlDQp0aGUgcm9idXN0bmVzcyBvZiB0aGUgY2x1c3RlcmluZyByZXN1bHRzIHVuZGVyIHZhcmlvdXMgc3Vic2V0cyBhbmQgdG8NCm9idGFpbiBpbnNpZ2h0cyBpbnRvIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGEuIEZ1cnRoZXJtb3JlLCB3ZSB3aWxsDQp0YWtlIDEwMCBzYW1wbGVzIGZyb20gb3VyIGRhdGEgc2V0IHRvIGVuc3VyZSB0aGF0IHRoZSBjbHVzdGVyaW5nIHBsb3QNCmRvZXNuJ3QgZ2V0IHRvbyBjcm93ZGVkLg0KDQpgYGB7cn0NCnNldC5zZWVkKDcpDQoNCiMgU3BlY2lmeSB0aGUgbnVtYmVyIG9mIHJvd3MgeW91IHdhbnQgdG8gc2FtcGxlDQpudW1fcm93cyA8LSAxMDANCg0KIyBVc2Ugc2FtcGxlIHdpdGggdGhlIHNwZWNpZmllZCBzZWVkDQppZHggPC0gc2FtcGxlKDE6ZGltKEhvYmJ5X0RhdGEyKVsxXSwgbnVtX3Jvd3MpDQoNCkhvYmJ5X0RhdGEzIDwtIEhvYmJ5X0RhdGEyW2lkeCwgXQ0KDQpwYW0ucmVzdWx0IDwtIHBhbShIb2JieV9EYXRhMywzKQ0KI1Nob3cgdGhlIHNpbGhvdXRlZSBwbG90IG9mIFBBTSBBTkQgY2x1c3RlcnMNCnBsb3QocGFtLnJlc3VsdCkNCmBgYA0KDQpgYGB7cn0NCiMgRXh0cmFjdCB0aGUgY2x1c2luZm8gY29tcG9uZW50DQpjbHVzaW5mbyA8LSBwYW0ucmVzdWx0JGNsdXNpbmZvDQoNCiMgQ2FsY3VsYXRlIHRoZSB0b3RhbCB3aXRoaW4tY2x1c3RlciBzdW0gb2Ygc3F1YXJlcw0KdG90X3dpdGhpbnNzIDwtIHN1bShjbHVzaW5mb1ssICJzaXplIl0gKiBjbHVzaW5mb1ssICJhdl9kaXNzIl1eMikNCg0KIyBQcmludCB0aGUgcmVzdWx0DQpwcmludCh0b3Rfd2l0aGluc3MpDQpgYGANCg0KVGhlIG91dHB1dCBvZiB0aGUgY29kZSwgZGlzcGxheWluZyBhIHNpbGhvdWV0dGUgcGxvdCBvZiB0aGUgUEFNDQpjbHVzdGVycywgaW5kaWNhdGVzIHRoYXQgdGhlIGNsdXN0ZXJzIGFyZSByZWxhdGl2ZWx5IGNsb3NlIHRvIGVhY2gNCm90aGVyLCB3aXRoIGEgc2xpZ2h0IG92ZXJsYXAgYmV0d2VlbiB0d28gY2x1c3RlcnMuIFRoZSBzaWxob3VldHRlIHBsb3QNCnZpc3VhbGx5IHJlcHJlc2VudHMgaG93IHdlbGwtZGVmaW5lZCBhbmQgc2VwYXJhdGVkIHRoZSBjbHVzdGVycyBhcmUuDQpXaGlsZSB0aGUgc2xpZ2h0IG92ZXJsYXAgc3VnZ2VzdHMgdGhhdCB0aGUgbmF0dXJhbCBncm91cGluZyB3aXRoaW4gdGhlDQpkYXRhc2V0IG1heSBub3QgYmUgZW50aXJlbHkgZGlzdGluY3QsIGl0IGRvZXMgbm90IG5lZ2F0aXZlbHkgYWZmZWN0IHRoZQ0Kb3ZlcmFsbCBxdWFsaXR5IG9mIHRoZSBjbHVzdGVycy4gSW4gZmFjdCwgdGhlIG9ic2VydmVkIG92ZXJsYXAgbWlnaHQNCmluZGljYXRlIHNoYXJlZCBjaGFyYWN0ZXJpc3RpY3MgYmV0d2VlbiBhZGphY2VudCBjbHVzdGVycywgZWZmZWN0aXZlbHkNCmNhcHR1cmluZyBtZWFuaW5nZnVsIHBhdHRlcm5zIGFuZCBncm91cGluZ3MgdGhhdCByZWZsZWN0IHRoZSBpbnRyaWNhY2llcw0Kb2YgcmVhbC13b3JsZCBwaGVub21lbmEgbm90IGNvbmZpbmVkIHRvIHN0cmljdCBib3VuZGFyaWVzLg0KDQpiQ3ViZWQNCg0KYGBge3J9DQpjbHVzdGVyX2Fzc2lnbm1lbnRzIDwtIGMocGFtLnJlc3VsdCRjbHVzdGVyKQ0Kc2V0LnNlZWQoNykNCg0KIyBTcGVjaWZ5IHRoZSBudW1iZXIgb2Ygcm93cyB5b3Ugd2FudCB0byBzYW1wbGUNCm51bV9yb3dzIDwtIDEwMA0KDQojIFVzZSBzYW1wbGUgd2l0aCB0aGUgc3BlY2lmaWVkIHNlZWQNCmlkeCA8LSBzYW1wbGUoMTpkaW0oSG9iYnlfcHJvYylbMV0sIG51bV9yb3dzKQ0KDQojIFNlbGVjdCB0aGUgc2FtcGxlZCByb3dzIGZyb20gSG9iYnlfcHJvYw0KSG9iYnlfRGF0YTQgPC0gSG9iYnlfcHJvY1tpZHgsIF0NCmdyb3VuZF90cnV0aF9sYWJlbHMgPC0gYyhIb2JieV9EYXRhNCkNCg0KIyBDcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggY2x1c3RlciBhc3NpZ25tZW50cyBhbmQgZ3JvdW5kIHRydXRoIGxhYmVscw0KZGF0YXNldCA8LSBkYXRhLmZyYW1lKGNsdXN0ZXIgPSBjbHVzdGVyX2Fzc2lnbm1lbnRzLCBsYWJlbCA9IGdyb3VuZF90cnV0aF9sYWJlbHMpDQoNCiMgQ2FsY3VsYXRlIEJDdWJlZCBwcmVjaXNpb24gYW5kIHJlY2FsbA0KY2FsY3VsYXRlX2JjdWJlZF9tZXRyaWNzIDwtIGZ1bmN0aW9uKGRhdGFzZXQpIHsNCiAgbiA8LSBucm93KGRhdGFzZXQpDQogIHByZWNpc2lvbl9zdW0gPC0gMCANCiAgcmVjYWxsX3N1bSA8LSAwDQogIA0KICAgZm9yIChpIGluIDE6bikgew0KICAgIGNsdXN0ZXIgPC0gZGF0YXNldCRjbHVzdGVyW2ldIA0KICAgIGxhYmVsIDwtIGRhdGFzZXQkbGFiZWxbaV0NCiAgICANCiAgICAjIENvdW50IHRoZSBudW1iZXIgb2YgaXRlbXMgZnJvbSB0aGUgc2FtZSBjYXRlZ29yeSBpbiBpdHMgY2x1c3Rlcg0KICAgIHNhbWVfY2F0ZWdvcnkgPC0gc3VtKGRhdGFzZXQkbGFiZWxbZGF0YXNldCRjbHVzdGVyID09IGNsdXN0ZXJdID09IGxhYmVsKSAgIA0KICAgIA0KICAgICMgQ291bnQgdGhlIG51bWJlciBvZiBpdGVtcyBpbiBpdHMgY2x1c3RlciAgICANCiAgICBzYW1lX2NsdXN0ZXIgPC0gc3VtKGRhdGFzZXQkY2x1c3RlciA9PSBjbHVzdGVyKQ0KICAgIA0KICAgICMgQ291bnQgdGhlIG51bWJlciBvZiBpdGVtcyBpbiBpdHMgY2F0ZWdvcnkNCiAgICB0b3RhbF9zYW1lX2NhdGVnb3J5IDwtIHN1bShkYXRhc2V0JGxhYmVsID09IGxhYmVsKSAgIA0KICAgIA0KICAgICMgQ2FsY3VsYXRlIHByZWNpc2lvbiBhbmQgcmVjYWxsIA0KICAgIHByZWNpc2lvbl9zdW0gPC0gcHJlY2lzaW9uX3N1bSArIHNhbWVfY2F0ZWdvcnkgLyBzYW1lX2NsdXN0ZXINCiAgICByZWNhbGxfc3VtIDwtIHJlY2FsbF9zdW0gKyBzYW1lX2NhdGVnb3J5IC8gdG90YWxfc2FtZV9jYXRlZ29yeSANCiAgICB9DQogICMgRW5kIGxvb3AgDQogIA0KICAjIENhbGN1bGF0ZSBhdmVyYWdlIHByZWNpc2lvbiBhbmQgcmVjYWxsICANCiAgcHJlY2lzaW9uIDwtIHByZWNpc2lvbl9zdW0gLyBuDQogIHJlY2FsbCA8LSByZWNhbGxfc3VtIC8gbiANCiAgcmV0dXJuKGxpc3QocHJlY2lzaW9uID0gcHJlY2lzaW9uLCByZWNhbGwgPSByZWNhbGwpKX0NCg0KICAjIENhbGN1bGF0ZSBCQ3ViZWQgcHJlY2lzaW9uIGFuZCByZWNhbGwNCiAgbWV0cmljcyA8LSBjYWxjdWxhdGVfYmN1YmVkX21ldHJpY3MoZGF0YXNldCkNCiAgcHJlY2lzaW9uIDwtIG1ldHJpY3MkcHJlY2lzaW9uDQogIHJlY2FsbCA8LSBtZXRyaWNzJHJlY2FsbA0KDQojIFByaW50IHRoZSByZXN1bHRzDQogIGNhdCgiQkN1YmVkIFByZWNpc2lvbj0gIiwgcHJlY2lzaW9uLCAiQU5EIEJDdWJlZCBSZWNhbGw9ICIsIHJlY2FsbCwgIlxuIikNCmBgYA0KDQpXaGlsZSBwcmVjaXNpb24gaGlnaGxpZ2h0cyByb29tIGZvciBiZXR0ZXIgYWNjdXJhY3kgaW4gaWRlbnRpZnlpbmcNCnNpbWlsYXIgaXRlbXMsIHRoZSBoaWdoZXIgcmVjYWxsIGluZGljYXRlcyB0aGUgYWxnb3JpdGhtJ3MgY2FwYWJpbGl0eSB0bw0KY2F0Y2ggYSBnb29kIGFtb3VudCBvZiBhY3R1YWwgc2ltaWxhcml0aWVzIHdpdGhpbiBjbHVzdGVycy4NCg0KDQoNCg0KKiotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLWdyb3VwIGludG8gaz00IGNsdXN0ZXJzLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKioNCg0KYGBge3J9DQpzZXQuc2VlZCg3KQ0KDQojIFNwZWNpZnkgdGhlIG51bWJlciBvZiByb3dzIHlvdSB3YW50IHRvIHNhbXBsZQ0KbnVtX3Jvd3MgPC0gMTAwDQoNCiMgVXNlIHNhbXBsZSB3aXRoIHRoZSBzcGVjaWZpZWQgc2VlZA0KaWR4IDwtIHNhbXBsZSgxOmRpbShIb2JieV9EYXRhMilbMV0sIG51bV9yb3dzKQ0KDQpIb2JieV9EYXRhMyA8LSBIb2JieV9EYXRhMltpZHgsIF0NCg0KcGFtLnJlc3VsdCA8LSBwYW0oSG9iYnlfRGF0YTMsNCkNCiNTaG93IHRoZSBzaWxob3V0ZWUgcGxvdCBvZiBQQU0gQU5EIGNsdXN0ZXJzDQpwbG90KHBhbS5yZXN1bHQpDQpgYGANCg0KDQpgYGB7cn0NCiMgRXh0cmFjdCB0aGUgY2x1c2luZm8gY29tcG9uZW50DQpjbHVzaW5mbyA8LSBwYW0ucmVzdWx0JGNsdXNpbmZvDQoNCiMgQ2FsY3VsYXRlIHRoZSB0b3RhbCB3aXRoaW4tY2x1c3RlciBzdW0gb2Ygc3F1YXJlcw0KdG90X3dpdGhpbnNzIDwtIHN1bShjbHVzaW5mb1ssICJzaXplIl0gKiBjbHVzaW5mb1ssICJhdl9kaXNzIl1eMikNCg0KIyBQcmludCB0aGUgcmVzdWx0DQpwcmludCh0b3Rfd2l0aGluc3MpDQpgYGANClRoZSBvdXRwdXQgc3VnZ2VzdHMgdGhhdCB0aGUgZGF0YXNldCBtYXkgZXhoaWJpdCBhIGRlZ3JlZSBvZiBvdmVybGFwIG9yDQpzaW1pbGFyaXR5IGFtb25nIG9ic2VydmF0aW9ucy4gVGhlIG92ZXJsYXBwaW5nIGNsdXN0ZXJzIG1heSBpbmRpY2F0ZQ0KY2hhbGxlbmdlcyBpbiBhY2hpZXZpbmcgYSBjbGVhciBzZXBhcmF0aW9uIGFtb25nIHRoZXNlIGdyb3Vwcy4gVGhlDQpwbGFjZW1lbnQgb2Ygb25lIGNsdXN0ZXIgb24gdG9wIG9mIHR3byBvdGhlcnMgaW1wbGllcyB0aGF0IHRoZSBtZWRvaWQgb2YNCnRoaXMgY2x1c3RlciBtaWdodCBiZSBuZWFyIHBvaW50cyBiZWxvbmdpbmcgdG8gdGhvc2UgdHdvIG5laWdoYm9yaW5nDQpjbHVzdGVycy4gVGhpcyBjb3VsZCBiZSBkdWUgdG8gdGhlIG5hdHVyZSBvZiB0aGUgZGF0YS4NCg0KYGBge3J9DQpjbHVzdGVyX2Fzc2lnbm1lbnRzIDwtIHBhbS5yZXN1bHQkY2x1c3RlcmluZw0KDQojIEFkZCBjbHVzdGVyIGFzc2lnbm1lbnRzIHRvIHRoZSBvcmlnaW5hbCBkYXRhDQpIb2JieV9EYXRhX3dpdGhfY2x1c3RlcnMgPC0gY2JpbmQoSG9iYnlfRGF0YTMsIENsdXN0ZXIgPSBmYWN0b3IoY2x1c3Rlcl9hc3NpZ25tZW50cykpDQoNCiMgRXhhbXBsZTogUGFpciBwbG90IGZvciB0aGUgZmlyc3QgZm91ciBmZWF0dXJlcw0KcGFpcnMoSG9iYnlfRGF0YV93aXRoX2NsdXN0ZXJzWywgMTo0XSwgY29sID0gY2x1c3Rlcl9hc3NpZ25tZW50cykNCg0KDQoNCmBgYA0KDQoNCg0KDQoNCkJDdWJlZA0KYGBge3J9DQpjbHVzdGVyX2Fzc2lnbm1lbnRzIDwtIGMocGFtLnJlc3VsdCRjbHVzdGVyKSANCiANCnNldC5zZWVkKDcpIA0KIA0KIyBTcGVjaWZ5IHRoZSBudW1iZXIgb2Ygcm93cyB5b3Ugd2FudCB0byBzYW1wbGUgDQpudW1fcm93cyA8LSAxMDAgDQogDQojIFVzZSBzYW1wbGUgd2l0aCB0aGUgc3BlY2lmaWVkIHNlZWQgDQppZHggPC0gc2FtcGxlKDE6ZGltKEhvYmJ5X3Byb2MpWzFdLCBudW1fcm93cykgDQogDQojIFNlbGVjdCB0aGUgc2FtcGxlZCByb3dzIGZyb20gSG9iYnlfcHJvYyANCkhvYmJ5X0RhdGE0IDwtIEhvYmJ5X3Byb2NbaWR4LCBdIA0KDQojIENyZWF0ZSBhIGRhdGEgZnJhbWUgd2l0aCBjbHVzdGVyIGFzc2lnbm1lbnRzIGFuZCBncm91bmQgdHJ1dGggbGFiZWxzDQpkYXRhc2V0IDwtIGRhdGEuZnJhbWUoY2x1c3RlciA9IGNsdXN0ZXJfYXNzaWdubWVudHMsIGxhYmVsID0gZ3JvdW5kX3RydXRoX2xhYmVscykNCg0KIyBDYWxjdWxhdGUgQkN1YmVkIHByZWNpc2lvbiBhbmQgcmVjYWxsDQpjYWxjdWxhdGVfYmN1YmVkX21ldHJpY3MgPC0gZnVuY3Rpb24oZGF0YXNldCkgew0KICBuIDwtIG5yb3coZGF0YXNldCkNCiAgcHJlY2lzaW9uX3N1bSA8LSAwDQogIHJlY2FsbF9zdW0gPC0gMA0KIA0KICBmb3IgKGkgaW4gMTpuKSB7DQogICAgY2x1c3RlciA8LSBkYXRhc2V0JGNsdXN0ZXJbaV0NCiAgICBsYWJlbCA8LSBkYXRhc2V0JGxhYmVsW2ldDQogICANCiAgICAjIENvdW50IHRoZSBudW1iZXIgb2YgaXRlbXMgZnJvbSB0aGUgc2FtZSBjYXRlZ29yeSBpbiBpdHMgY2x1c3Rlcg0KICAgIHNhbWVfY2F0ZWdvcnkgPC0gc3VtKGRhdGFzZXQkbGFiZWxbZGF0YXNldCRjbHVzdGVyID09IGNsdXN0ZXJdID09IGxhYmVsKQ0KICAgDQogICAgIyBDb3VudCB0aGUgbnVtYmVyIG9mIGl0ZW1zIGluIGl0cyBjbHVzdGVyDQogICAgc2FtZV9jbHVzdGVyIDwtIHN1bShkYXRhc2V0JGNsdXN0ZXIgPT0gY2x1c3RlcikNCiAgIA0KICAgICMgQ291bnQgdGhlIG51bWJlciBvZiBpdGVtcyBpbiBpdHMgY2F0ZWdvcnkNCiAgICB0b3RhbF9zYW1lX2NhdGVnb3J5IDwtIHN1bShkYXRhc2V0JGxhYmVsID09IGxhYmVsKQ0KICAgDQogICAgIyBDYWxjdWxhdGUgcHJlY2lzaW9uIGFuZCByZWNhbGwNCiAgICBwcmVjaXNpb25fc3VtIDwtIHByZWNpc2lvbl9zdW0gKyBzYW1lX2NhdGVnb3J5IC8gc2FtZV9jbHVzdGVyDQogICAgcmVjYWxsX3N1bSA8LSByZWNhbGxfc3VtICsgc2FtZV9jYXRlZ29yeSAvIHRvdGFsX3NhbWVfY2F0ZWdvcnkNCiAgfQ0KICAjIEVuZCBsb29wDQogDQogICMgQ2FsY3VsYXRlIGF2ZXJhZ2UgcHJlY2lzaW9uIGFuZCByZWNhbGwNCiAgcHJlY2lzaW9uIDwtIHByZWNpc2lvbl9zdW0gLyBuDQogIHJlY2FsbCA8LSByZWNhbGxfc3VtIC8gbg0KIA0KICByZXR1cm4obGlzdChwcmVjaXNpb24gPSBwcmVjaXNpb24sIHJlY2FsbCA9IHJlY2FsbCkpDQp9DQoNCiMgQ2FsY3VsYXRlIEJDdWJlZCBwcmVjaXNpb24gYW5kIHJlY2FsbA0KbWV0cmljcyA8LSBjYWxjdWxhdGVfYmN1YmVkX21ldHJpY3MoZGF0YXNldCkNCnByZWNpc2lvbiA8LSBtZXRyaWNzJHByZWNpc2lvbg0KcmVjYWxsIDwtIG1ldHJpY3MkcmVjYWxsDQoNCiMgUHJpbnQgdGhlIHJlc3VsdHMNCmNhdCgiQkN1YmVkIFByZWNpc2lvbj0gIiwgcHJlY2lzaW9uLCAiQU5EIEJDdWJlZCBSZWNhbGw9ICIsIHJlY2FsbCwgIlxuIikNCmBgYA0KDQpUaGUgcmVzdWx0cyBpbmRpY2F0ZSBjaGFsbGVuZ2VzIGluIGNsdXN0ZXJpbmcgcGVyZm9ybWFuY2UuIFRoZSBsb3cNCnByZWNpc2lvbiBzdWdnZXN0cyBhIHNpZ25pZmljYW50IHJhdGUgb2YgbWlzY2xhc3NpZmljYXRpb24sIHdoaWxlIHRoZQ0KcmVsYXRpdmVseSBsb3cgcmVjYWxsIGluZGljYXRlcyB0aGF0IHNvbWUgaW5zdGFuY2VzIHdpdGhpbiB0aGUgc2FtZQ0KZ3JvdXAgYXJlIG1pc3NlZCBvciBpbmNvcnJlY3RseSBhc3NpZ25lZCB0byBvdGhlciBjbHVzdGVycy4gVGhlc2UNCnJlc3VsdHMgaGlnaGxpZ2h0IGxpbWl0YXRpb25zIGluIGFjY3VyYXRlbHkgY2FwdHVyaW5nIHRoZSBkYXRhJ3MNCnVuZGVybHlpbmcgc3RydWN0dXJlLg0KDQoNCg0KKiotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tZ3JvdXAgaW50byBrPTIgY2x1c3RlcnMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSoqDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoNykNCg0KIyBTcGVjaWZ5IHRoZSBudW1iZXIgb2Ygcm93cyB5b3Ugd2FudCB0byBzYW1wbGUNCm51bV9yb3dzIDwtIDEwMA0KDQojIFVzZSBzYW1wbGUgd2l0aCB0aGUgc3BlY2lmaWVkIHNlZWQNCmlkeCA8LSBzYW1wbGUoMTpkaW0oSG9iYnlfRGF0YTIpWzFdLCBudW1fcm93cykNCg0KSG9iYnlfRGF0YTMgPC0gSG9iYnlfRGF0YTJbaWR4LCBdDQoNCnBhbS5yZXN1bHQgPC0gcGFtKEhvYmJ5X0RhdGEzLDIpDQojU2hvdyB0aGUgc2lsaG91dGVlIHBsb3Qgb2YgUEFNIEFORCBjbHVzdGVycw0KcGxvdChwYW0ucmVzdWx0KQ0KYGBgDQoNCg0KYGBge3J9DQojIEV4dHJhY3QgdGhlIGNsdXNpbmZvIGNvbXBvbmVudA0KY2x1c2luZm8gPC0gcGFtLnJlc3VsdCRjbHVzaW5mbw0KDQojIENhbGN1bGF0ZSB0aGUgdG90YWwgd2l0aGluLWNsdXN0ZXIgc3VtIG9mIHNxdWFyZXMNCnRvdF93aXRoaW5zcyA8LSBzdW0oY2x1c2luZm9bLCAic2l6ZSJdICogY2x1c2luZm9bLCAiYXZfZGlzcyJdXjIpDQoNCiMgUHJpbnQgdGhlIHJlc3VsdA0KcHJpbnQodG90X3dpdGhpbnNzKQ0KYGBgDQpBbiBvdmVybGFwIGJldHdlZW4gY2x1c3RlcnMgaW1wbGllcyB0aGF0IHRoZXJlIGlzIGFtYmlndWl0eSBpbiB0aGUNCmFzc2lnbm1lbnQgb2YgZGF0YSBwb2ludHMgdG8gY2x1c3RlcnMsIGFuZCB0aGUgY2x1c3RlcnMgbWF5IG5vdCBiZQ0Kc3VmZmljaWVudGx5IGRpc3RpbmN0LiBJbiBzdWNoIGNhc2VzLCBpdCBtaWdodCBiZSBuZWVkZWQgdG8gcmVjb25zaWRlcg0KdGhlIG51bWJlciBvZiBjbHVzdGVycyBzaW5jZSB0aGUgZ29hbCBpcyB0byBmaW5kIGEgYmFsYW5jZSwgYnV0IHRvbyBmZXcNCmNsdXN0ZXJzIHJlc3VsdCBpbiBvdmVyc2ltcGxpZmljYXRpb24sIGFzIGluZGljYXRlZCBieSB0aGUgb2JzZXJ2ZWQNCm92ZXJsYXAsIGFuZCBpdCBpcyBldmlkZW50IHRoYXQgZm9ybWluZyBvbmx5IHR3byBjbHVzdGVycyBtYXkgbm90IGJlDQpzdWZmaWNpZW50IHRvIHJlcHJlc2VudCB0aGUgaW5oZXJlbnQgc3RydWN0dXJlIG9mIHRoZSBkYXRhc2V0Lg0KDQoNCiNCQ3ViZWQNCg0KYGBge3J9DQpjbHVzdGVyX2Fzc2lnbm1lbnRzIDwtIGMocGFtLnJlc3VsdCRjbHVzdGVyKSANCiANCnNldC5zZWVkKDcpIA0KIA0KIyBTcGVjaWZ5IHRoZSBudW1iZXIgb2Ygcm93cyB5b3Ugd2FudCB0byBzYW1wbGUgDQpudW1fcm93cyA8LSAxMDAgDQogDQojIFVzZSBzYW1wbGUgd2l0aCB0aGUgc3BlY2lmaWVkIHNlZWQgDQppZHggPC0gc2FtcGxlKDE6ZGltKEhvYmJ5X3Byb2MpWzFdLCBudW1fcm93cykgDQogDQojIFNlbGVjdCB0aGUgc2FtcGxlZCByb3dzIGZyb20gSG9iYnlfcHJvYyANCkhvYmJ5X0RhdGE0IDwtIEhvYmJ5X3Byb2NbaWR4LCBdIA0KDQojIENyZWF0ZSBhIGRhdGEgZnJhbWUgd2l0aCBjbHVzdGVyIGFzc2lnbm1lbnRzIGFuZCBncm91bmQgdHJ1dGggbGFiZWxzDQpkYXRhc2V0IDwtIGRhdGEuZnJhbWUoY2x1c3RlciA9IGNsdXN0ZXJfYXNzaWdubWVudHMsIGxhYmVsID0gZ3JvdW5kX3RydXRoX2xhYmVscykNCg0KIyBDYWxjdWxhdGUgQkN1YmVkIHByZWNpc2lvbiBhbmQgcmVjYWxsDQpjYWxjdWxhdGVfYmN1YmVkX21ldHJpY3MgPC0gZnVuY3Rpb24oZGF0YXNldCkgew0KICBuIDwtIG5yb3coZGF0YXNldCkNCiAgcHJlY2lzaW9uX3N1bSA8LSAwDQogIHJlY2FsbF9zdW0gPC0gMA0KIA0KICBmb3IgKGkgaW4gMTpuKSB7DQogICAgY2x1c3RlciA8LSBkYXRhc2V0JGNsdXN0ZXJbaV0NCiAgICBsYWJlbCA8LSBkYXRhc2V0JGxhYmVsW2ldDQogICANCiAgICAjIENvdW50IHRoZSBudW1iZXIgb2YgaXRlbXMgZnJvbSB0aGUgc2FtZSBjYXRlZ29yeSBpbiBpdHMgY2x1c3Rlcg0KICAgIHNhbWVfY2F0ZWdvcnkgPC0gc3VtKGRhdGFzZXQkbGFiZWxbZGF0YXNldCRjbHVzdGVyID09IGNsdXN0ZXJdID09IGxhYmVsKQ0KICAgDQogICAgIyBDb3VudCB0aGUgbnVtYmVyIG9mIGl0ZW1zIGluIGl0cyBjbHVzdGVyDQogICAgc2FtZV9jbHVzdGVyIDwtIHN1bShkYXRhc2V0JGNsdXN0ZXIgPT0gY2x1c3RlcikNCiAgIA0KICAgICMgQ291bnQgdGhlIG51bWJlciBvZiBpdGVtcyBpbiBpdHMgY2F0ZWdvcnkNCiAgICB0b3RhbF9zYW1lX2NhdGVnb3J5IDwtIHN1bShkYXRhc2V0JGxhYmVsID09IGxhYmVsKQ0KICAgDQogICAgIyBDYWxjdWxhdGUgcHJlY2lzaW9uIGFuZCByZWNhbGwNCiAgICBwcmVjaXNpb25fc3VtIDwtIHByZWNpc2lvbl9zdW0gKyBzYW1lX2NhdGVnb3J5IC8gc2FtZV9jbHVzdGVyDQogICAgcmVjYWxsX3N1bSA8LSByZWNhbGxfc3VtICsgc2FtZV9jYXRlZ29yeSAvIHRvdGFsX3NhbWVfY2F0ZWdvcnkNCiAgfQ0KICAjIEVuZCBsb29wDQogDQogICMgQ2FsY3VsYXRlIGF2ZXJhZ2UgcHJlY2lzaW9uIGFuZCByZWNhbGwNCiAgcHJlY2lzaW9uIDwtIHByZWNpc2lvbl9zdW0gLyBuDQogIHJlY2FsbCA8LSByZWNhbGxfc3VtIC8gbg0KIA0KICByZXR1cm4obGlzdChwcmVjaXNpb24gPSBwcmVjaXNpb24sIHJlY2FsbCA9IHJlY2FsbCkpDQp9DQoNCiMgQ2FsY3VsYXRlIEJDdWJlZCBwcmVjaXNpb24gYW5kIHJlY2FsbA0KbWV0cmljcyA8LSBjYWxjdWxhdGVfYmN1YmVkX21ldHJpY3MoZGF0YXNldCkNCnByZWNpc2lvbiA8LSBtZXRyaWNzJHByZWNpc2lvbg0KcmVjYWxsIDwtIG1ldHJpY3MkcmVjYWxsDQoNCiMgUHJpbnQgdGhlIHJlc3VsdHMNCmNhdCgiQkN1YmVkIFByZWNpc2lvbj0gIiwgcHJlY2lzaW9uLCAiQU5EIEJDdWJlZCBSZWNhbGw9ICIsIHJlY2FsbCwgIlxuIikNCmBgYA0KDQpUaGUgcmVsYXRpdmVseSBoaWdoIHJlY2FsbCBjb3VsZCBiZSBpbmZsdWVuY2VkIGJ5IHRoZSBzcGVjaWZpYyBjaG9pY2Ugb2YNCnR3byBjbHVzdGVycyBzaW5jZSBpdCBpcyBzZW5zaXRpdmUgdG8gdGhlIG51bWJlciBvZiBjbHVzdGVycy4gSW4gdGhlDQpjb250ZXh0IG9mIGEgdHdvLWNsdXN0ZXIgc29sdXRpb24sIHRoZSByZWNhbGwgc2NvcmUgcmVmbGVjdHMgaG93IHdlbGwNCnRoZSBhbGdvcml0aG0gZ3JvdXBzIGRhdGEgcG9pbnRzIGZyb20gdGhlIHNhbWUgY2xhc3MgaW50byBvbmUgb2YgdGhlIHR3bw0KaWRlbnRpZmllZCBjbHVzdGVycy4gUmVjYWxsIHN1Z2dlc3RzIHRoYXQgYSBzaWduaWZpY2FudCBwb3J0aW9uIG9mIGRhdGENCnBvaW50cyBmcm9tIHRoZSBzYW1lIGdyb3VuZCB0cnV0aCBjbGFzcyBhcmUgaW5kZWVkIGdyb3VwZWQgdG9nZXRoZXIgaW4NCm9uZSBvZiB0aGUgdHdvIGNsdXN0ZXJzLiBIb3dldmVyLCBpdCdzIGltcG9ydGFudCB0byBub3RlIHRoYXQgdGhlIGxvdw0KcHJlY2lzaW9uIHNjb3JlICgwLjA0MzIpIGluZGljYXRlcyBhIGxhY2sgb2YgaG9tb2dlbmVpdHkgd2l0aGluIHRoZQ0KaWRlbnRpZmllZCBjbHVzdGVycywgaW1wbHlpbmcgdGhhdCB0aGUgY2x1c3RlcnMgY29udGFpbiBhIG1peCBvZiBkYXRhDQpwb2ludHMgZnJvbSBkaWZmZXJlbnQgZ3JvdW5kIHRydXRoIGNsYXNzZXMuDQoNCg0KDQoNCg0KDQoNCioqQVMgQSBTdW1tYXJ5IEZvciBjbHVzdHJpbmcqKg0KU2lsaG91ZXR0ZSBhbmFseXNpcyBtZWFzdXJlcyBob3cgc2ltaWxhciBhbiBvYmplY3QgaXMgdG8gaXRzIG93biBjbHVzdGVyDQooY29oZXNpb24pIGNvbXBhcmVkIHRvIG90aGVyIGNsdXN0ZXJzIChzZXBhcmF0aW9uKS4gVGhlIHNpbGhvdWV0dGUgd2lkdGgNCnJhbmdlcyBmcm9tIC0xIHRvIDEsIHdoZXJlIGEgaGlnaCB2YWx1ZSBpbmRpY2F0ZXMgdGhhdCB0aGUgb2JqZWN0IGlzDQp3ZWxsIG1hdGNoZWQgdG8gaXRzIG93biBjbHVzdGVyIGFuZCBwb29ybHkgbWF0Y2hlZCB0byBuZWlnaGJvcmluZw0KY2x1c3RlcnMuIEluIG91ciBwcm9qZWN0IHdlIGRpZCBjbHVzdGVycyBmb3Igaz0zLCBrPTQsIGFuZCBrPTIgc2luY2UgaXRzDQpoYXZlIGhpZ2hlciBhdmVyYWdlIHNpbGhvdWV0dGUgdGhlbiBvdGhlciBudW1iZXIuDQoNCkZvciBrPTMsIHRoZSBhdmVyYWdlIHNpbGhvdWV0dGUgd2lkdGggaXMgMC4yMi4gDQpGb3Igaz00LCB0aGUgYXZlcmFnZSBzaWxob3VldHRlIHdpZHRoIGlzIDAuMTkuIA0KRm9yIGs9MiwgdGhlIGF2ZXJhZ2Ugc2lsaG91ZXR0ZSB3aWR0aCBpcyAwLjIxLg0KDQpBIGhpZ2hlciBhdmVyYWdlIHNpbGhvdWV0dGUgd2lkdGggZ2VuZXJhbGx5IGluZGljYXRlcyBiZXR0ZXItZGVmaW5lZA0KY2x1c3RlcnMuIFNvLCBpbiB0aGlzIGNhc2UsIGs9MyBoYXMgdGhlIGhpZ2hlc3QgYXZlcmFnZSBzaWxob3VldHRlDQp3aWR0aC4NCg0KQkN1YmVkIGlzIGEgY2x1c3RlcmluZyBldmFsdWF0aW9uIG1ldHJpYyB0aGF0IGNvbnNpZGVycyBib3RoIHByZWNpc2lvbg0KYW5kIHJlY2FsbC4gUHJlY2lzaW9uIG1lYXN1cmVzIHRoZSBhY2N1cmFjeSBvZiB0aGUgcG9zaXRpdmUgcHJlZGljdGlvbnMsDQp3aGlsZSByZWNhbGwgbWVhc3VyZXMgdGhlIGNvdmVyYWdlIG9mIHRoZSBhY3R1YWwgcG9zaXRpdmUgaW5zdGFuY2VzLg0KDQpGb3Igaz0zLCBCQkN1YmVkIFByZWNpc2lvbiA9IDAuMDQ4OCBCQ3ViZWQgUmVjYWxsID0gMC40OTMyDQpGb3Igaz00LEJDdWJlZCBQcmVjaXNpb24gPSAwLjA1MDUgQkN1YmVkIFJlY2FsbCA9IDAuNDEwMQ0KRm9yIGs9MiwgQkN1YmVkUHJlY2lzaW9uID0gMC4wNDMyIEJDdWJlZCBSZWNhbGwgPSAwLjYyNTINCg0KVGhlc2UgbWV0cmljcyBtZWFzdXJlIGhvdyB3ZWxsIHRoZSBjbHVzdGVyaW5nIGFsaWducyB3aXRoIHRoZSBncm91bmQNCnRydXRoLiBIaWdoZXIgcHJlY2lzaW9uIGluZGljYXRlcyBmZXdlciBmYWxzZSBwb3NpdGl2ZXMsIGFuZCBoaWdoZXINCnJlY2FsbCBpbmRpY2F0ZXMgZmV3ZXIgZmFsc2UgbmVnYXRpdmVzLiBIZXJlLCBrPTIgaGFzIHRoZSBoaWdoZXN0IHJlY2FsbA0KKDAuNjI1MiksIGJ1dCBrPTMgaGFzIGEgcmVhc29uYWJsZSBiYWxhbmNlIGJldHdlZW4gcHJlY2lzaW9uIGFuZCByZWNhbGwuDQoNCkluIGNvbmNsdXNpb24sIGs9MyBzZWVtcyB0byBiZSBhIHJlYXNvbmFibGUgY2hvaWNlLiBJdCBoYXMgYSBnb29kDQpzaWxob3VldHRlIHdpZHRoLCBhbmQgaXRzIEJDdWJlZCBQcmVjaXNpb24gYW5kIFJlY2FsbCB2YWx1ZXMgc3RyaWtlIGENCmJhbGFuY2UuDQoNCglLPTIJSz0zKEJFU1QpCUs9NA0KQXZlcmFnZSBTaWxob3VldHRlIHdpZHRoCTAuMjEJMC4yMgkwLjE5DQp0b3RhbCB3aXRoaW4tY2x1c3RlciBzdW0gb2Ygc3F1YXJlCTEwNTkuNg0KCTgzMy41NTg1DQoJNzYyLjg4OTUNCg0KQkN1YmVkIHByZWNpc2lvbgkwLjA0MzIJMC4wNDg4CTAuMDUwNQ0KQkN1YmVkIHJlY2FsbAkwLjYyNTIJMC40OTMyCTAuNDEwMQ0KVmlzdWFsaXphdGlvbglJbiB0aGUgZmlndXJlcyBhYm92ZS4NCg0KDQoqKkZpbmRpbmdzKioNCg0KfCAgICAgICAgICAgICAgICB8IDkwJS8xMCUgfCA5MCUvMTAlICB8ICA5MCUvMTAlICAgfCA4MCUvMjAlIHwgODAlLzIwJSAgfCAgODAlLzIwJSAgIHwgNzAlLzMwJSB8IDcwJS8zMCUgIHwgIDcwJS8zMCUgICB8DQp8Oi0tLS0tLS0tOnw6LS0tLS0tOnw6LS0tLS0tOnw6LS0tLS0tOnw6LS0tLS0tOnw6LS0tLS0tOnw6LS0tLS0tOnw6LS0tLS0tOnw6LS0tLS0tOnw6LS0tLS0tOnwNCnwgICAgICAgICAgICAgICAgfCAgIElHICAgIHwgSUcgcmF0aW8gfCBHaW5pIEluZGV4IHwgICBJRyAgICB8IElHIHJhdGlvIHwgR2luaSBJbmRleCB8ICAgSUcgICAgfCBJRyByYXRpbyB8IEdpbmkgSW5kZXggfA0KfCAgKipBY2N1cmFjeSoqICB8ICAwLjg3MiAgfCAgMC44OTQ0ICB8ICAwLjkxODc1ICAgfCAgMC45MDUgIHwgIDAuODg4ICAgfCAgIDAuOTA2ICAgIHwgIDAuODkzICB8ICAwLjg5MzkgIHwgICAwLjkxMSAgICB8DQp8ICoqcHJlY2lzaW9uKiogIHwgIDAuODU2ICB8ICAwLjg5MiAgIHwgIDAuOTE4NTAgICB8ICAwLjg5OCAgfCAgMC44ODEgICB8ICAgMC45MDUgICAgfCAgMC44ODcgIHwgIDAuODkwICAgfCAgIDAuOTA5ICAgIHwNCnwgKipzZW5zaXRpdml5KiogfCAgMC44NTYgIHwgIDAuODkxICAgfCAgMC45MTkyNyAgIHwgIDAuODk4ICB8ICAwLjg5MCAgIHwgICAwLjkwOSAgICB8ICAwLjg4NyAgfCAgMC45MDIgICB8ICAgMC45MTIgICAgfA0KfCAqKnNwZWNpZmljaXkqKiB8ICAwLjkxOSAgfCAgMC45NDUgICB8ICAgMC45NTc4ICAgfCAgMC45MzcgIHwgIDAuOTQzICAgfCAgIDAuOTUyICAgIHwgIDAuOTI5ICB8ICAwLjk0NyAgIHwgICAwLjk1NCAgICB8DQoNCioqUmVmZXJlbmNlcyoqDQoNCg0KKipXaGF0IGlzIHRoZSBiZXN0IGZvciBvdXIgZGF0YXNldD8qKg0KDQpzaW5jZSBXZSB1c2VkIHRoZSBJbmZvcm1hdGlvbiBHYWluLCBHaW5pIEluZGV4LCBhbmQgR2FpbiBSYXRpbyBhcyB0aHJlZSBpbXBvcnRhbnQgbWV0cmljcyBpbiBvdXIgY2F0ZWdvcmljYWwgZGF0YSBjbGFzc2lmaWNhdGlvbiBhcHByb2FjaCB0byBkZXRlcm1pbmUgaG93IGltcG9ydGFudCBjZXJ0YWluIHZhcmlhYmxlcyB3ZXJlIGluIHByZWRpY3RpbmcgaG9iYnkgY2F0ZWdvcmllcy4gVGhlIGRhdGFzZXQgd2FzIHNwbGl0IGludG8gdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cyBzbyB0aGF0IGRpZmZlcmVudCBzdWJzZXRzIG9mIHRoZSBkYXRhIGNvdWxkIGJlIHVzZWQgdG8gdHJhaW4gY2xhc3NpZmljYXRpb24gYWxnb3JpdGhtcy4gVGhyZWUgbWV0cmljcyB3ZXJlIHVzZWQgdG8gZXZhbHVhdGUgdGhlIHBlcmZvcm1hbmNlIG9mIHRoZSBtb2RlbDogb3ZlcmFsbCBhY2N1cmFjeSwgcmVjYWxsLCBhbmQgcHJlY2lzaW9uLldlIGV4YW1pbmVkIGFjY3VyYWN5IGF0IHNldmVyYWwgbWVhc3VyZXMgb2YgdHJlZSBzZWxlY3Rpb24sIFByb3ZpZGUgdXMgd2l0aCBtb3JlIGFjY3VyYXRlIHJlc3VsdHMsIHNwZWNpZmljYWxseSBhdCB0aGUgcHJ1bmluZyBwYXJhbWV0ZXJzIG9mIDAuOTAsIDAuOTEsIGFuZCAwLjg5LCBpbiBvcmRlci5UaGUgbWV0aG9kaWNhbCBldmFsdWF0aW9uIHlpZWxkZWQgaW5zaWdodGZ1bCBpbmZvcm1hdGlvbiBvbiBob3cgd2VsbCBlYWNoIHNlbGVjdGlvbiBtZWFzdXJlIHBlcmZvcm1lZCBpbiBwcm9kdWNpbmcgcHJlY2lzZSBwcmVkaWN0aW9ucyB3aXRoaW4gdGhlIGRhdGFzZXQuIGhpZ2ggYWNjdXJhY3kgc3Ryb25nbHkgc3VnZ2VzdCB0aGF0LCBmb3Igb3VyIGRhdGFzZXQsIGNsYXNzaWZpY2F0aW9uIGlzIGEgYmV0dGVyIG1ldGhvZG9sb2d5IHRoYW4gY2x1c3RlcmluZywgc2luY2UgdGhlIGNsYXNzaWZpY2F0aW9uIGdpdmUgdXMgYSBnb29kIHJlc3VsdCB0aGVuIGNsdXN0cmluZy4NCg0KDQo=
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyAqKktpZHMgSG9iYnkgUHJlZGljdGlvbiBEYXRhc2V0KioNCg0KDQoqKlByb2JsZW0qKg0KDQpDaGlsZHJlbiBvZnRlbiBmaW5kIHRoZW1zZWx2ZXMgYXQgYSBjcm9zc3JvYWRzIHdoZW4gaXQgY29tZXMgdG8gZGlzY292ZXJpbmcgdGhlaXIgcGFzc2lvbnMgb3IgaG9iYmllcywgYmUgaXQgaW4gYWNhZGVtaWNzLCBhcnRzLCBvciBzcG9ydHMuIFJlY29nbml6aW5nIHRoZSBpbXBvcnRhbmNlIG9mIGd1aWRpbmcgY2hpbGRyZW4gdG93YXJkcyBhY3Rpdml0aWVzIHRoZXkgYXJlIHBhc3Npb25hdGUgYWJvdXQ7IHdlIGhhdmUgY3VyYXRlZCBhIGRhdGFzZXQgb2J0YWluZWQgdGhyb3VnaCBzdXJ2ZXlzIGNvbmR1Y3RlZCB3aXRoIHBhcmVudHMuIFRoaXMgZGF0YXNldCBjb21waWxlcyB2YWx1YWJsZSBpbmZvcm1hdGlvbiBhYm91dCBjaGlsZHJlbidzIHByZWZlcmVuY2VzLCBlbmFibGluZyB0aGUgY3JlYXRpb24gb2YgYSBjbGFzc2lmaWNhdGlvbiBtb2RlbCBhaW1lZCBhdCBwcmVkaWN0aW5nIGtpZHMnIGhvYmJpZXMuIA0KDQpJbiB0aGlzIGV4cGxvcmF0b3J5IGpvdXJuZXksIHdlIGRlbHZlIGludG8gdGhlIGRhdGFzZXQgdG8gdW5jb3ZlciBwYXR0ZXJucyBhbmQgaW5zaWdodHMgdGhhdCBjYW4gYXNzaXN0IHBhcmVudHMgaW4gdW5kZXJzdGFuZGluZyB0aGVpciBjaGlsZCdzIGluY2xpbmF0aW9ucyBiZXR0ZXIuIFRocm91Z2ggdGhlIGFwcGxpY2F0aW9uIG9mIGNsdXN0ZXJpbmcgdGVjaG5pcXVlcywgd2UgYWltIHRvIGNhdGVnb3JpemUgY2hpbGRyZW4gYmFzZWQgb24gbnVtYmVyIG9mIGNvbHVtbnMgKEZhdl9zdWIgLCBTY2hvbGFyc2hpcCwgZXRjLi4pLiBUaGUgdWx0aW1hdGUgZ29hbCBpcyB0byBwcm92aWRlIHBhcmVudHMgd2l0aCBtZWFuaW5nZnVsIHJlY29tbWVuZGF0aW9ucywgZm9zdGVyaW5nIGFuIGVudmlyb25tZW50IHdoZXJlIGNoaWxkcmVuIGNhbiB0aHJpdmUgaW4gYWN0aXZpdGllcyB0aGF0IGdlbnVpbmVseSByZXNvbmF0ZSB3aXRoIHRoZWlyIGludGVyZXN0cy4gDQoNCkJ5IGFuYWx5emluZyB0aGlzIGRhdGFzZXQsIHdlIGhvcGUgdG8gb2ZmZXIgaW5zaWdodGZ1bCBpbmZvcm1hdGlvbiB0aGF0IHdpbGwgYWlkIHBhcmVudHMgaW4gZ3VpZGluZyB0aGVpciBraWRzIHRvd2FyZCB0aGUgcmlnaHQgaG9iYmllcy4gDQoNCg0KKipEYXRhIE1pbmluZyBUYXNrKioNCg0KVGhlIGRhdGEgbWluaW5nIHRhc2sgYXQgaGFuZCByZXZvbHZlcyBhcm91bmQgcHJlZGljdGluZyBraWRzJyBob2JiaWVzIGJhc2VkIG9uIGEgZGF0YXNldCBuYW1lZCAiSG9iYnlfRGF0YSwiIG9idGFpbmVkIHRocm91Z2ggc3BlY2lmaWMgcXVlc3Rpb25zIHBvc2VkIHRvIHRoZWlyIHBhcmVudHMgcmVnYXJkaW5nIHByZWZlcmVuY2VzLCBjYXBhYmlsaXRpZXMsIGFuZCBhY2hpZXZlbWVudHMuIFRoaXMgZGF0YXNldCBzZXJ2ZXMgYXMgdGhlIGZvdW5kYXRpb24gZm9yIHR3byBwcmltYXJ5IGRhdGEgbWluaW5nIHRhc2tzOiBjbGFzc2lmaWNhdGlvbiBhbmQgY2x1c3RlcmluZy4gSW4gdGhlIGNsYXNzaWZpY2F0aW9uIHByb2Nlc3MsIHRoZSBnb2FsIGlzIHRvIHRyYWluIGEgbWFjaGluZSBsZWFybmluZyBtb2RlbCB0byBiZSBjYXBhYmxlIG9mIGFjY3VyYXRlbHkgcHJlZGljdGluZyBhIGNoaWxkJ3MgaG9iYnkgYXMgZWl0aGVyICJhY2FkZW1pYywiICJhcnQsIiBvciAic3BvcnRzLiIgVGhpcyByZXF1aXJlcyB1c2luZyB0aGUgZGF0YSBjb2xsZWN0ZWQgZnJvbSBwYXJlbnRzIHRvIGVzdGFibGlzaCBwYXR0ZXJucyBhbmQgcmVsYXRpb25zaGlwcyB0aGF0IGd1aWRlIHRoZSBtb2RlbCBpbiBtYWtpbmcgYWNjdXJhdGUgcHJlZGljdGlvbnMuIENvbmN1cnJlbnRseSwgdGhlIGNsdXN0ZXJpbmcgcHJvY2VzcyBpbnZvbHZlcyBwYXJ0aXRpb25pbmcgdGhlIGRhdGFzZXQgaW50byBtZWFuaW5nZnVsIGNsdXN0ZXJzLCBncm91cGluZyB0b2dldGhlciBjaGlsZHJlbiB3aXRoIHNpbWlsYXIgY2hhcmFjdGVyaXN0aWNzIG9yIHByZWZlcmVuY2VzLiBUaHJvdWdoIHRoZXNlIGR1YWwgYXBwcm9hY2hlcywgdGhlIGRhdGEgbWluaW5nIHRhc2sgYWltcyB0byBkZXZlbG9wIGEgcm9idXN0IHByZWRpY3RpdmUgbW9kZWwgY2FwYWJsZSBvZiBhY2N1cmF0ZWx5IGNhdGVnb3JpemluZyBjaGlsZHJlbidzIGhvYmJpZXMgaW4gb3JkZXIgdG8gdW5jb3ZlciB0aGUgaW5oZXJlbnQgc3RydWN0dXJlcywgcGF0dGVybnMsIGFuZCBhc3NvY2lhdGlvbnMgd2l0aGluIHRoZSBkYXRhc2V0LCBjb250cmlidXRpbmcgdG8gYSBkZWVwZXIgdW5kZXJzdGFuZGluZyBvZiB0aGUgZGl2ZXJzZSBpbnRlcmVzdHMgYW5kIGVuZ2FnZW1lbnQgbGV2ZWxzIG9mIHRoZSB5b3VuZyBwb3B1bGF0aW9uIGluIGFjYWRlbWljLCBhcnRpc3RpYywgYW5kIHNwb3J0cy1yZWxhdGVkIGFjdGl2aXRpZXMuDQogDQogDQoNCg0KKipEYXRhKioNCg0KVGhlIHNvdXJjZTogaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9kYXRhc2V0cy9hYnRhYm0vaG9iYnktcHJlZGljdGlvbi1iYXNpYyANCk51bWJlciBvZiBvYmplY3RzOiAxNjAxIA0KDQpOdW1iZXIgb2YgYXR0cmlidXRlczogMTQgDQoNCg0KKipEZXNjcmlwdGlvbjoqKg0KRm9sbG93aW5nIHRoZSBzZWxlY3Rpb24gb2Ygb3VyIGRhdGEgc2V0ICgiSG9iYnlfRGF0YSIpIHdoaWNoIHByZWRpY3RzIGtpZHMnIGhvYmJpZXMsIHRoYXQgd2FzIGNvbGxlY3RlZCBieSBhc2tpbmcgdGhlaXIgcGFyZW50cyBzcGVjaWZpYyBxdWVzdGlvbnMgYWJvdXQgdGhlaXIga2lkJ3MgcHJlZmVyZW5jZXMsIGNhcGFiaWxpdGllcywgYW5kIGFjaGlldmVtZW50cy4gVG8gaGVscCB1cyB0cmFpbiB0aGUgbWFjaGluZSB0byBwcmVkaWN0IHRoZSBraWQncyBob2JieS4gV2Ugd2lsbCBiZWdpbiB0byBwcmVwcm9jZXNzIGFuZCBhbmFseXplIHRoZSBkYXRhLg0KDQoNCg0KKipBdHRyaWJ1dGUqKiAqKkRlc2NyaXB0aW9uOioqDQoNCnwgQXR0cmlidXRlIG5hbWUgICAgICAgICB8ICoqRGVzY3JpcHRpb24qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAqKkRhdGEgdHlwZSoqIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLXwNCnwgT2x5bXBpYWRfUGFydGljaXBhdGlvbiB8IEhhcyB5b3VyIGNoaWxkIHBhcnRpY2lwYXRlZCBpbiBhbnkgU2NpZW5jZS9NYXRocyAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgU2Nob2xhcnNoaXAgICAgICAgICAgICB8IEhhcyBoZS9zaGUgcmVjZWl2ZWQgYW55IHNjaG9sYXJzaGlwPyAgICAgICAgICAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgU2Nob29sICAgICAgICAgICAgICAgICB8IExvdmUncyBnb2luZyB0byBzY2hvb2w/ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgRmF2X3N1YiAgICAgICAgICAgICAgICB8IFdoYXQgaXMgaGlzL2hlciBmYXZvcml0ZSBzdWJqZWN0PyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBDYXRlZ29yaWNhbCAgIHwNCnwgUHJvamVjdHMgICAgICAgICAgICAgICB8IEhhcyBkb25lIGFueSBwcm9qZWN0cyB1bmRlciBhY2FkZW1pY3MgYmVmb3JlPyAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgR3Jhc3BfcG93ICAgICAgICAgICAgICB8IEhpcy9IZXIgR3Jhc3BpbmcgcG93ZXIgKDEtNikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBPcmRpbmFsICAgICAgIHwNCnwgVGltZV9zcHJ0ICAgICAgICAgICAgICB8IEhvdyBtdWNoIHRpbWUgZG9lcyBoZS9zaGUgc3BlbmQgcGxheWluZyBvdXRkb29yL2luZG9vciBnYW1lcz8gfCBPcmRpbmFsICAgICAgIHwNCnwgTWVkYWxzICAgICAgICAgICAgICAgICB8IE1lZGFscyB3b24gaW4gU3BvcnRzPyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgQ2FyZWVyX3NwcnQgICAgICAgICAgICB8IFdhbnQncyB0byBwdXJzdWUgaGlzL2hlciBjYXJlZXIgaW4gc3BvcnRzPyAgICAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgQWN0X3NwcnQgICAgICAgICAgICAgICB8IFJlZ3VsYXIgaW4gaGlzL2hlciBzcG9ydHMgYWN0aXZpdGllcz8gICAgICAgICAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgRmFudF9hcnRzICAgICAgICAgICAgICB8IExvdmUgY3JlYXRpbmcgZmFudGFzeSBwYWludGluZ3M/ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBCb29sZWFuICAgICAgIHwNCnwgV29uX2FydHMgICAgICAgICAgICAgICB8IFdvbiBhcnQgY29tcGV0aXRpb25zPyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBPcmRpbmFsICAgICAgIHwNCnwgVGltZV9hcnQgICAgICAgICAgICAgICB8IFRpbWUgdXRpbGl6ZWQgaW4gQXJ0cz8gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBPcmRpbmFsICAgICAgIHwNCnwgUHJlZGljdGVkIEhvYmJ5ICAgICAgICB8IHByZWRpY3Rpb25zIGZvciB0aGUgaG9iYnkgdGhhdCB0aGUga2lkIHdvdWxkbCBpa2UgICAgICAgICAgICAgfCBDYXRlZ29yaWNhbCAgIHwNCj09PT09PT0NCj4+Pj4+Pj4gU3Rhc2hlZCBjaGFuZ2VzDQoNCioqR2VuZXJhbCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgZGF0YSBzZXQ6KioNCg0KYGBge3J9DQpzdHIoSG9iYnlfRGF0YSkNCmBgYA0KDQotICAgKipzYW1wbGVzIG9mIHJhdyBkYXRhc2V0OioqDQoNCmBgYHtyfQ0Kc2FtcGxlKEhvYmJ5X0RhdGEpDQpgYGANCg0KLSAgICoqdmFyaWFibGVzIGRpc3RyaWJ1dGlvbjoqKg0KDQogICAgSW4gb3VyIGRhdGFzZXQsIG51bWVyaWMgdmFyaWFibGVzIGFyZSBub3QgYXZhaWxhYmxlOyBpbnN0ZWFkLCB3ZSBoYXZlIHRocmVlIG9yZGluYWwgdmFyaWFibGVzLiBEdWUgdG8gdGhlIG5hdHVyZSBvZiBvdXIgZGF0YSB0eXBlcywgY2VydGFpbiB0eXBlcyBvZiBncmFwaHMsIHN1Y2ggYXMgc2NhdHRlciBwbG90cyBhbmQgYm94IHBsb3RzLCB3ZXJlIG5vdCBzdWl0YWJsZSBmb3Igb3VyIGFuYWx5c2lzLg0KDQp2YXJpYWJsZXMgZGlzdHJpYnV0aW9uIG9mIFRpbWVfc3BydDoNCg0KYGBge3J9DQppbnN0YWxsLnBhY2thZ2VzKCJtYWdyaXR0ciIpICMgaW5zdGFsbCBvbmx5IG9uZSB0aW1lIHRoZW4gcHV0IHRoaXMgY29tbWFuZCBhcyBjb21tZW50IGFmdGVyIGluc3RhbGxhdGlvbg0KbGlicmFyeShtYWdyaXR0cikgIyMgZm9yIHBpcGUgb3BlcmF0aW9ucw0KSG9iYnlfRGF0YSRUaW1lX2FydCAlPiUgZGVuc2l0eSgpICU+JSBwbG90KG1haW49J3ZhcmlhYmxlcyBkaXN0cmlidXRpb24gb2YgVGltZV9hcnQnKQ0KDQpgYGANCg0KSW4gdGhlICJUaW1lX2FydCIgdmFyaWFibGUsIHBhcmVudHMgd2VyZSByZXF1ZXN0ZWQgdG8gYXNzZXNzIHRoZSB0aW1lIHRoZWlyIGNoaWxkIGRlZGljYXRlZCB0byBhcnRpc3RpYyBwdXJzdWl0cyBsaWtlIHBhaW50aW5nIG9yIHBhcGVyIGNyYWZ0aW5nLCB1c2luZyBhIHNjYWxlIHJhbmdpbmcgZnJvbSAxIHRvIDYsIHdoZXJlIDYgcmVwcmVzZW50cyB0aGUgaGlnaGVzdCBsZXZlbCBvZiBpbnZvbHZlbWVudC4gSXQncyB3b3J0aCBub3RpbmcgdGhhdCB0aGUgY29uY2VudHJhdGlvbiBvZiBsb3dlciByYXRpbmdzIGF0IHRoZSBsb3dlciBlbmQgb2YgdGhlIHNjYWxlICgxKSBpcyBxdWl0ZSBwcm9ub3VuY2VkLCBhbmQgdGhpcyB0ZW5kZW5jeSBtYXkgYmUgYXR0cmlidXRlZCB0byB0aGUgaW5oZXJlbnQgaW5jbGluYXRpb24gb2YgY2hpbGRyZW4gdG93YXJkcyBwaHlzaWNhbCBhY3Rpdml0aWVzLg0KDQp2YXJpYWJsZXMgZGlzdHJpYnV0aW9uIG9mIFRpbWVfYXJ0Og0KDQpgYGB7cn0NCkhvYmJ5X0RhdGEkVGltZV9zcHJ0ICU+JSBkZW5zaXR5KCkgJT4lIHBsb3QobWFpbj0ndmFyaWFibGVzIGRpc3RyaWJ1dGlvbiBvZiBUaW1lX3NwcnQnKQ0KYGBgDQoNClBhcmVudHMgd2VyZSByZXF1ZXN0ZWQgdG8gYXNzZXNzIHRoZWlyIGNoaWxkcmVuJ3MgaW52b2x2ZW1lbnQgaW4gc3BvcnRzIG9uIGEgc2NhbGUgZnJvbSAxIHRvIDYgd2l0aGluIHRoZSAiVGltZV9zcHJ0IiB2YXJpYWJsZS4gTm90YWJseSwgdGhlIG1vc3QgcHJldmFsZW50IHJhbmtpbmcgd2FzIDMsIHN1Z2dlc3RpbmcgYSBtb2RlcmF0ZSBsZXZlbCBvZiBzcG9ydHMgcGFydGljaXBhdGlvbi4gSXQncyBpbnRlcmVzdGluZyB0byBvYnNlcnZlIHRoYXQgdGhlIGRpc3RyaWJ1dGlvbiBleGhpYml0cyBhIHNoYXBlIGFraW4gdG8gYSBiZWxsIGN1cnZlLCBpbmRpY2F0aW5nIHRoYXQgYSBzdWJzdGFudGlhbCBwcm9wb3J0aW9uIG9mIGNoaWxkcmVuIGhhdmUgYSBnZW51aW5lIGxvdmUgZm9yIHNwb3J0cy4NCg0KdmFyaWFibGVzIGRpc3RyaWJ1dGlvbiBvZiBHcmFzcF9wb3c6DQoNCmBgYHtyfQ0KSG9iYnlfRGF0YSRHcmFzcF9wb3cgJT4lIGRlbnNpdHkoKSAlPiUgcGxvdChtYWluPSd2YXJpYWJsZXMgZGlzdHJpYnV0aW9uIG9mIEdyYXNwX3BvdycpDQpgYGANCg0KVGhlIGRlbnNpdHkgZ3JhcGggZGVwaWN0aW5nIHBhcmVudHMnIHJhdGluZ3Mgb2YgdGhlaXIgY2hpbGRyZW4ncyBncmFzcCBwb3dlciwgd2hpY2ggcmFuZ2VzIGZyb20gMSB0byA2LCBpbGx1c3RyYXRlcyBhIHRyZW5kIHdoZXJlIHRoZSBtb3N0IGNvbW1vbiByYXRpbmcgaXMgbGV2ZWwgMywgZm9sbG93ZWQgYnkgbGV2ZWwgNCwgbGV2ZWwgNSwgbGV2ZWwgMiwgbGV2ZWwgMSwgYW5kIGxldmVsIDYuIFRoaXMgZGlzdHJpYnV0aW9uIGluZGljYXRlcyB0aGF0IGEgc3Vic3RhbnRpYWwgcHJvcG9ydGlvbiBvZiBwYXJlbnRzIGJlbGlldmUgdGhlaXIgY2hpbGRyZW4gcG9zc2VzcyBncmFzcCBwb3dlciB0aGF0IGlzIGF2ZXJhZ2Ugb3Igc2xpZ2h0bHkgYWJvdmUgYXZlcmFnZSAobGV2ZWxzIDMgYW5kIDQpLCB3aXRoIGZld2VyIGNoaWxkcmVuIHJhdGVkIGF0IHRoZSBleHRyZW1lcyAobGV2ZWxzIDEsIDIsIDUsIGFuZCA2KS4NCg0KdmFyaWFibGVzIGRpc3RyaWJ1dGlvbiBvZiB0aGUgY2xhc3MgbGFiZWwgJ1ByZWRpY3RlZCBIb2JieSc6DQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKSAjIGluc3RhbGwgb25seSBvbmUgdGltZSB0aGVuIHB1dCB0aGlzIGNvbW1hbmQgYXMgY29tbWVudCBhZnRlciBpbnN0YWxsYXRpb24NCmxpYnJhcnkoZHBseXIpDQoNCmRhdGFzZXQyIDwtIEhvYmJ5X0RhdGEgJT4lIHNhbXBsZV9uKDE2MDApDQp0YWJsZShkYXRhc2V0MiRgUHJlZGljdGVkIEhvYmJ5YCkgJT4lIHBpZSgpDQp0YWIgPC0gZGF0YXNldDIkYFByZWRpY3RlZCBIb2JieWAgJT4lIHRhYmxlKCkNCnByZWNlbnRhZ2VzIDwtIHRhYiAlPiUgcHJvcC50YWJsZSgpICU+JSByb3VuZCgzKSAqIDEwMCANCnR4dCA8LSBwYXN0ZTAobmFtZXModGFiKSwgJ1xuJywgcHJlY2VudGFnZXMsICclJykNCnBpZSh0YWIsIGxhYmVscz10eHQpDQpgYGANCg0KVGhlIHBpZSBjaGFydCBpbGx1c3RyYXRlcyB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBjbGFzcyBsYWJlbCAnUHJlZGljdGVkIEhvYmJ5Jy4gSXQncyBldmlkZW50IHRoYXQgYSBzdWJzdGFudGlhbCBwb3J0aW9uLCBhcHByb3hpbWF0ZWx5IDQzLjclLCBvZiB0aGUgY2hpbGRyZW4ncyBob2JiaWVzIGFyZSBhY2FkZW1pYyBpbiBuYXR1cmUsIGluZGljYXRpbmcgYSBzdHJvbmcgaW50ZXJlc3QgaW4gZWR1Y2F0aW9uYWwgcHVyc3VpdHMuIEFkZGl0aW9uYWxseSwgMzAuOCUgb2YgdGhlIGtpZHMgYXJlIGVuZ2FnZWQgaW4gc3BvcnRzLCByZWZsZWN0aW5nIGEgc2lnbmlmaWNhbnQgaW5jbGluYXRpb24gdG93YXJkcyBwaHlzaWNhbCBhY3Rpdml0aWVzLiBBcnRzLXJlbGF0ZWQgaG9iYmllcyBhY2NvdW50IGZvciAyNS42JSBvZiB0aGUgdG90YWwsIHRoZSBkaXN0cmlidXRpb24gcmVmbGVjdHMgYSBoYXJtb25pb3VzIGJsZW5kIG9mIGhvYmJpZXMuIFRoaXMgYmFsYW5jZWQgZGlzdHJpYnV0aW9uIG5vdCBvbmx5IHNpZ25pZmllcyBhIHZhcmlldHkgb2YgaW50ZXJlc3RzIGJ1dCBhbHNvIGluZGljYXRlcyBhIHdlbGwtcm91bmRlZCBlbmdhZ2VtZW50IG9mIGNoaWxkcmVuIGluIGFjYWRlbWljLCBwaHlzaWNhbCwgYW5kIGNyZWF0aXZlIGFjdGl2aXRpZXMuDQoNCg0KKipTdGF0aXN0aWNhbCBtZWFzdXJlcyA6KioNCg0KYGBge3J9DQpmaW5kX21vZGUgPC0gZnVuY3Rpb24oeCkgew0KICB1IDwtIHVuaXF1ZSh4KQ0KICB0YWIgPC0gdGFidWxhdGUobWF0Y2goeCwgdSkpDQogIHVbdGFiID09IG1heCh0YWIpXQ0KfQ0KDQpmaW5kX21vZGUoSG9iYnlfRGF0YSRUaW1lX2FydCkNCiMgVXBkYXRlZCB1cHN0cmVhbQ0KaGlzdChIb2JieV9EYXRhJFRpbWVfYXJ0KQ0KDQojU3Rhc2hlZCBjaGFuZ2VzDQpgYGANCg0KVGhlIGhpc3RvZ3JhbSBtYWtlcyBpdCBldmlkZW50IHRoYXQgdGhlIG1vZGUgaXMgZXF1YWwgdG8gMSwgYW5kIHdlIGJlbGlldmUgdGhhdCB0aGUgaGlnaCBmcmVxdWVuY3kgb2YgcGFyZW50cyByYW5raW5nICIxIiBhcyB0aGUgbW9zdCBjaG9zZW4gcmFuayBpbiB0aGUgIlRpbWVfYXJ0IiB2YXJpYWJsZSBjb3VsZCBiZSBhdHRyaWJ1dGVkIHRvIHNldmVyYWwgZmFjdG9ycy4gSXQgbWlnaHQgaW5kaWNhdGUgdGhhdCBhIHNpZ25pZmljYW50IG51bWJlciBvZiBwYXJlbnRzIHBlcmNlaXZlIHRoZWlyIGNoaWxkcmVuJ3MgaW52b2x2ZW1lbnQgaW4gYXJ0IGFjdGl2aXRpZXMgYXMgcmVsYXRpdmVseSBsb3csIHBvc3NpYmx5IGR1ZSB0byB0aW1lIGNvbnN0cmFpbnRzLCBhY2FkZW1pYyBwcmlvcml0aWVzLCBvciBhIGxpbWl0ZWQgaW50ZXJlc3QgaW4gYXJ0LiBBbHRlcm5hdGl2ZWx5LCBpdCBjb3VsZCByZWZsZWN0IHRoYXQgcGFyZW50cyB2YWx1ZSBhIG1vcmUgYmFsYW5jZWQgYXBwcm9hY2ggdG8gdGhlaXIgY2hpbGRyZW4ncyBhY3Rpdml0aWVzLCB3aXRoIGEgdmFyaWV0eSBvZiBpbnRlcmVzdHMgYW5kIHJlc3BvbnNpYmlsaXRpZXMgc2hhcmluZyB0aGVpciB0aW1lLiBUaGlzIHRyZW5kIGNvdWxkIGFsc28gcmVzdWx0IGZyb20gYSBjdWx0dXJhbCBvciBlZHVjYXRpb25hbCBlbXBoYXNpcyBvbiBvdGhlciBzdWJqZWN0cyBhbmQgZXh0cmFjdXJyaWN1bGFyIGFjdGl2aXRpZXMgdGhhdCBjb21wZXRlIGZvciBhIGNoaWxkJ3MgdGltZSwgcG90ZW50aWFsbHkgbGVhZGluZyB0byBhIGxvd2VyIHJhbmtpbmcgZm9yIGFydC1yZWxhdGVkIGFjdGl2aXRpZXMuDQoNCmBgYHtyfQ0KZmluZF9tb2RlKEhvYmJ5X0RhdGEkR3Jhc3BfcG93KQ0KaGlzdChIb2JieV9EYXRhJEdyYXNwX3BvdykNCmBgYA0KDQpSYW5rIDMsIGluIHRoaXMgY29udGV4dCwgbWF5IGhhdmUgYmVlbiB0aGUgbW9zdCBjaG9zZW4gcmFuayBiZWNhdXNlIGl0IGxpa2VseSByZXByZXNlbnRzIGFuIGF2ZXJhZ2Ugb3IgbW9kZXJhdGUgbGV2ZWwgb2YgZ3Jhc3AgcG93ZXIuIFBhcmVudHMgbWF5IGhhdmUgYXNzZXNzZWQgdGhlaXIgY2hpbGRyZW4ncyBncmFzcCBwb3dlciBhcyBuZWl0aGVyIGV4Y2VwdGlvbmFsbHkgc3Ryb25nIChyYW5rIDUgb3IgNikgbm9yIHBhcnRpY3VsYXJseSB3ZWFrIChyYW5rIDEgb3IgMiksIHJlc3VsdGluZyBpbiB0aGUgcHJlZmVyZW5jZSBmb3IgdGhlIG1pZGRsZS1yYW5raW5nIG9wdGlvbi4gVGhpcyBjaG9pY2UgY291bGQgcmVmbGVjdCBhIHBlcmNlcHRpb24gdGhhdCB0aGVpciBjaGlsZHJlbidzIGdyYXNwIHBvd2VyIGZhbGxzIHdpdGhpbiBhIHR5cGljYWwgb3IgZXhwZWN0ZWQgcmFuZ2UsIG1ha2luZyBpdCB0aGUgbW9zdCBjb21tb24gcmF0aW5nLg0KDQpgYGB7cn0NCmZpbmRfbW9kZShIb2JieV9EYXRhJFRpbWVfc3BydCkNCmhpc3QoSG9iYnlfRGF0YSRUaW1lX3NwcnQpDQpgYGANCg0KQXMgc2hvd24gaW4gdGhlIGhpc3RvZ3JhbSB0aGUgcmFuayAzIG9uIGEgc2NhbGUgb2YgMSB0byA2IHdhcyBsaWtlbHkgdGhlIG1vc3QgY2hvc2VuIHJhbmsgZm9yIGFzc2Vzc2luZyBjaGlsZHJlbidzIGludm9sdmVtZW50IGluIHNwb3J0cyBiZWNhdXNlIGl0IHJlcHJlc2VudHMgYSBiYWxhbmNlZCBtaWRkbGUgZ3JvdW5kLiBQYXJlbnRzIG1heSBoYXZlIHBlcmNlaXZlZCByYW5rIDMgYXMgaW5kaWNhdGluZyB0aGF0IHRoZWlyIGNoaWxkcmVuIGFyZSBtb2RlcmF0ZWx5IGludm9sdmVkIGluIHNwb3J0cywgbm90IGV4Y2Vzc2l2ZWx5IGNvbW1pdHRlZCBvciBkaXNpbnRlcmVzdGVkLiBUaGlzIG1pZGRsZS1vZi10aGUtcm9hZCByYW5raW5nIHJlZmxlY3RzIGEgY29tbW9uIHBlcnNwZWN0aXZlIHRoYXQgbWFueSBwYXJlbnRzIG1heSBob2xkLCBjb25zaWRlcmluZyB0aGF0IGV4dHJlbWUgcmFua2luZ3MsIHN1Y2ggYXMgMSBvciA2LCBtaWdodCBzdWdnZXN0IGVpdGhlciBhIGxhY2sgb2YgaW52b2x2ZW1lbnQgb3IgYW4gZXhjZXNzaXZlIGZvY3VzIG9uIHNwb3J0cywgd2hpY2ggbWF5IG5vdCBhbGlnbiB3aXRoIHRoZWlyIHBlcmNlcHRpb24gb2YgdGhlaXIgY2hpbGQncyBvdmVyYWxsIHdlbGwtcm91bmRlZCBkZXZlbG9wbWVudC4NCg0KVGhlIGhpc3RvZ3JhbSBncmFwaCBmb3IgIlRpbWVfYXJ0IiB2YXJpYWJsZQ0KYGBge3J9DQpoaXN0KEhvYmJ5X0RhdGEkVGltZV9hcnQpDQpgYGANCg0KVGhlIGhpc3RvZ3JhbSBtYWtlcyBpdCBldmlkZW50IHRoYXQgdGhlIG1vZGUgaXMgZXF1YWwgdG8gMSwgYW5kIHdlIGJlbGlldmUgdGhhdCB0aGUgaGlnaCBmcmVxdWVuY3kgb2YgcGFyZW50cyByYW5raW5nICIxIiBhcyB0aGUgbW9zdCBjaG9zZW4gcmFuayBpbiB0aGUgIlRpbWVfYXJ0IiB2YXJpYWJsZSBjb3VsZCBiZSBhdHRyaWJ1dGVkIHRvIHNldmVyYWwgZmFjdG9ycy4gSXQgbWlnaHQgaW5kaWNhdGUgdGhhdCBhIHNpZ25pZmljYW50IG51bWJlciBvZiBwYXJlbnRzIHBlcmNlaXZlIHRoZWlyIGNoaWxkcmVuJ3MgaW52b2x2ZW1lbnQgaW4gYXJ0IGFjdGl2aXRpZXMgYXMgcmVsYXRpdmVseSBsb3csIHBvc3NpYmx5IGR1ZSB0byB0aW1lIGNvbnN0cmFpbnRzLCBhY2FkZW1pYyBwcmlvcml0aWVzLCBvciBhIGxpbWl0ZWQgaW50ZXJlc3QgaW4gYXJ0LiBBbHRlcm5hdGl2ZWx5LCBpdCBjb3VsZCByZWZsZWN0IHRoYXQgcGFyZW50cyB2YWx1ZSBhIG1vcmUgYmFsYW5jZWQgYXBwcm9hY2ggdG8gdGhlaXIgY2hpbGRyZW4ncyBhY3Rpdml0aWVzLCB3aXRoIGEgdmFyaWV0eSBvZiBpbnRlcmVzdHMgYW5kIHJlc3BvbnNpYmlsaXRpZXMgc2hhcmluZyB0aGVpciB0aW1lLiBUaGlzIHRyZW5kIGNvdWxkIGFsc28gcmVzdWx0IGZyb20gYSBjdWx0dXJhbCBvciBlZHVjYXRpb25hbCBlbXBoYXNpcyBvbiBvdGhlciBzdWJqZWN0cyBhbmQgZXh0cmFjdXJyaWN1bGFyIGFjdGl2aXRpZXMgdGhhdCBjb21wZXRlIGZvciBhIGNoaWxkJ3MgdGltZSwgcG90ZW50aWFsbHkgbGVhZGluZyB0byBhIGxvd2VyIHJhbmtpbmcgZm9yIGFydC1yZWxhdGVkIGFjdGl2aXRpZXMuDQoNCg0KYGBge3J9DQpmaW5kX21vZGUoSG9iYnlfRGF0YSRHcmFzcF9wb3cpDQpgYGANClRoZSBoaXN0b2dyYW0gZ3JhcGggZm9yICJHcmFzcF9wb3ciIHZhcmlhYmxlDQpgYGB7cn0NCmhpc3QoSG9iYnlfRGF0YSRHcmFzcF9wb3cpDQpgYGANClJhbmsgMywgaW4gdGhpcyBjb250ZXh0LCBtYXkgaGF2ZSBiZWVuIHRoZSBtb3N0IGNob3NlbiByYW5rIGJlY2F1c2UgaXQgbGlrZWx5IHJlcHJlc2VudHMgYW4gYXZlcmFnZSBvciBtb2RlcmF0ZSBsZXZlbCBvZiBncmFzcCBwb3dlci4gUGFyZW50cyBtYXkgaGF2ZSBhc3Nlc3NlZCB0aGVpciBjaGlsZHJlbidzIGdyYXNwIHBvd2VyIGFzIG5laXRoZXIgZXhjZXB0aW9uYWxseSBzdHJvbmcgKHJhbmsgNSBvciA2KSBub3IgcGFydGljdWxhcmx5IHdlYWsgKHJhbmsgMSBvciAyKSwgcmVzdWx0aW5nIGluIHRoZSBwcmVmZXJlbmNlIGZvciB0aGUgbWlkZGxlLXJhbmtpbmcgb3B0aW9uLiBUaGlzIGNob2ljZSBjb3VsZCByZWZsZWN0IGEgcGVyY2VwdGlvbiB0aGF0IHRoZWlyIGNoaWxkcmVuJ3MgZ3Jhc3AgcG93ZXIgZmFsbHMgd2l0aGluIGEgdHlwaWNhbCBvciBleHBlY3RlZCByYW5nZSwgbWFraW5nIGl0IHRoZSBtb3N0IGNvbW1vbiByYXRpbmcuDQoNCg0KYGBge3J9DQpmaW5kX21vZGUoSG9iYnlfRGF0YSRUaW1lX3NwcnQpDQpgYGANCg0KVGhlIGhpc3RvZ3JhbSBncmFwaCBmb3IgIlRpbWVfc3BydCIgdmFyaWFibGUNCmBgYHtyfQ0KaGlzdChIb2JieV9EYXRhJFRpbWVfc3BydCkNCmBgYA0KQXMgc2hvd24gaW4gdGhlIGhpc3RvZ3JhbSB0aGUgcmFuayAzIG9uIGEgc2NhbGUgb2YgMSB0byA2IHdhcyBsaWtlbHkgdGhlIG1vc3QgY2hvc2VuIHJhbmsgZm9yIGFzc2Vzc2luZyBjaGlsZHJlbidzIGludm9sdmVtZW50IGluIHNwb3J0cyBiZWNhdXNlIGl0IHJlcHJlc2VudHMgYSBiYWxhbmNlZCBtaWRkbGUgZ3JvdW5kLiBQYXJlbnRzIG1heSBoYXZlIHBlcmNlaXZlZCByYW5rIDMgYXMgaW5kaWNhdGluZyB0aGF0IHRoZWlyIGNoaWxkcmVuIGFyZSBtb2RlcmF0ZWx5IGludm9sdmVkIGluIHNwb3J0cywgbm90IGV4Y2Vzc2l2ZWx5IGNvbW1pdHRlZCBvciBkaXNpbnRlcmVzdGVkLiBUaGlzIG1pZGRsZS1vZi10aGUtcm9hZCByYW5raW5nIHJlZmxlY3RzIGEgY29tbW9uIHBlcnNwZWN0aXZlIHRoYXQgbWFueSBwYXJlbnRzIG1heSBob2xkLCBjb25zaWRlcmluZyB0aGF0IGV4dHJlbWUgcmFua2luZ3MsIHN1Y2ggYXMgMSBvciA2LCBtaWdodCBzdWdnZXN0IGVpdGhlciBhIGxhY2sgb2YgaW52b2x2ZW1lbnQgb3IgYW4gZXhjZXNzaXZlIGZvY3VzIG9uIHNwb3J0cywgd2hpY2ggbWF5IG5vdCBhbGlnbiB3aXRoIHRoZWlyIHBlcmNlcHRpb24gb2YgdGhlaXIgY2hpbGQncyBvdmVyYWxsIHdlbGwtcm91bmRlZCBkZXZlbG9wbWVudC4NCg0KDQoqKkRhdGEgcHJlcHJvY2Vzc2luZyoqDQoNCg0KKiojMSMgRGF0YSBjbGVhbmluZzoqKg0KDQpEdXJpbmcgdGhlIGRhdGEgY2xlYW5pbmcgc3RhZ2UsIGZpbmRpbmcgYW5kIGZpeGluZyBmYXVsdHMsIGluY29uc2lzdGVuY2llcywgYW5kIGVycm9ycyBpbiBhIGRhdGFzZXQgaGVscHMgaXQgYmUgbW9yZSByZWxpYWJsZSBhbmQgb2YgaGlnaGVyIHF1YWxpdHkgZm9yIGFuYWx5c2lzIGFuZCBtb2RlbGluZy4gVGhlcmUgYXJlIG1ldGhvZHMgZm9yIGhhbmRsaW5nIG1pc3NpbmcgdmFsdWVzLCBkZXRlY3Rpbmcgb3V0bGllcnMsIHJlc29sdmluZyBpbmNvbnNpc3RlbmNpZXMsIGFuZCBzdGFuZGFyZGl6aW5nIGZvcm1hdHMuDQoNCmltcG9ydCBEYXRhc2V0IkhvYmJ5X0RhdGEiDQoNCmBgYHtyfQ0KDQpWaWV3KEhvYmJ5X0RhdGEpDQoNCnN0cihIb2JieV9EYXRhKQ0KYGBgDQoNCmNoZWNrIG1pc3NpbmcgdmFsdWUgOg0KDQpgYGB7cn0NCmlzLm5hKEhvYmJ5X0RhdGEpDQpgYGANCg0KZmluZCB0aGUgdG90YWwgbnVsbCB2YWx1ZXMgaW4gdGhlIGRhdGFzZXQ6DQoNCmBgYHtyfQ0Kc3VtKGlzLm5hKEhvYmJ5X0RhdGEpKQ0KYGBgDQoNCkRlc2NyaXB0aW9uOg0KDQpUaGlzIHN0YWdlIGludm9sdmVzIGNoZWNraW5nIGFuZCBkZWxldGluZyBmb3IgbnVsbCBhbmQgbWlzc2luZyB2YWx1ZXMgYmVjYXVzZSB0aGV5IG1pZ2h0IGhhdmUgYSBzaWduaWZpY2FudCBpbXBhY3Qgb24gdGhlIGRhdGEgYW5kIGNhdXNlIGVycm9ycyBhbmQgbmVnYXRpdmUgZWZmZWN0cyBpbiBzdWJzZXF1ZW50IHN0ZXBzLldlIHNpbXBseSBsb29rZWQgZm9yIG1pc3NpbmcgdmFsdWVzLCBhbmQgdGhlcmUgYXJlIGEgbWlzc2luZyB2YWx1ZXMgaW4gb3VyIGRhdGFzZXQuIEFjY29yZGluZyB0byBvdXIgaW52ZXN0aWdhdGlvbiwgdGhlIGRhdGFzZXQgZG9lcyBub3QgY29udGFpbiBhbnkgb3V0bGllcnMgc2luY2UgaXQgZG9lc24ndCBoYXZlIGEgbnVtZXJpY2FsIGRhdGEgdHlwZS4gQWRkaXRpb25hbGx5LCB0aGVyZSBhcmUgbm8gaW5jb25zaXN0ZW50IHZhbHVlcyBvciBvdGhlciBlcnJvcnMuDQoNCg0KRGF0YXNldCBhZnRlciBjbGVhbmluZyBzdGVwOg0KYGBge3J9DQpzYW1wbGUoSG9iYnlfRGF0YSkNCmBgYA0KDQoqKiMyI0VuY29kaW5nOioqDQpUaGlzIHN0ZXAgaW5jbHVkZXMgYSBDb252ZXJ0aW5nIGNhdGVnb3JpY2FsIG9yIG5vbi1udW1lcmljIGRhdGEgaW50byBhIG51bWVyaWNhbCBmb3JtYXQsIHdoaWNoIGlzIG5lY2Vzc2FyeSBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIHN1YnNlcXVlbnQgc3RlcHMgaW4gcHJlcHJvY2Vzc2luZy4NCg0KYGBge3J9DQpIb2JieV9EYXRhJE9seW1waWFkX1BhcnRpY2lwYXRpb24gPSBmYWN0b3IoSG9iYnlfRGF0YSRPbHltcGlhZF9QYXJ0aWNpcGF0aW9uLGxldmVscyA9IGMoIk5vIiwgIlllcyIpLCBsYWJlbHMgPSBjKDAsIDEpKQ0KSG9iYnlfRGF0YSRTY2hvbGFyc2hpcCA9IGZhY3RvcihIb2JieV9EYXRhJFNjaG9sYXJzaGlwICwgbGV2ZWxzID0gYygiTm8iLCAiWWVzIiksIGxhYmVscyA9IGMoMCwgMSkpDQpIb2JieV9EYXRhJFNjaG9vbCA9IGZhY3RvcihIb2JieV9EYXRhJFNjaG9vbCwgbGV2ZWxzID0gYygiTm8iLCAiWWVzIiksIGxhYmVscyA9IGMoMCwgMSkpDQpIb2JieV9EYXRhJFByb2plY3RzID0gZmFjdG9yKEhvYmJ5X0RhdGEkUHJvamVjdHMsIGxldmVscyA9IGMoIk5vIiwgIlllcyIpLCBsYWJlbHMgPSBjKDAsIDEpKQ0KSG9iYnlfRGF0YSRNZWRhbHMgPSBmYWN0b3IoSG9iYnlfRGF0YSRNZWRhbHMsIGxldmVscyA9IGMoIk5vIiwgIlllcyIpLCBsYWJlbHMgPSBjKDAsIDEpKQ0KSG9iYnlfRGF0YSRDYXJlZXJfc3BydCA9IGZhY3RvcihIb2JieV9EYXRhJENhcmVlcl9zcHJ0LCBsZXZlbHMgPSBjKCJObyIsICJZZXMiKSwgbGFiZWxzID0gYygwLCAxKSkNCkhvYmJ5X0RhdGEkQWN0X3NwcnQgPSBmYWN0b3IoSG9iYnlfRGF0YSRBY3Rfc3BydCwgbGV2ZWxzID0gYygiTm8iLCAiWWVzIiksIGxhYmVscyA9IGMoMCwgMSkpDQpIb2JieV9EYXRhJEZhbnRfYXJ0cyA9IGZhY3RvcihIb2JieV9EYXRhJEZhbnRfYXJ0cywgbGV2ZWxzID0gYygiTm8iLCAiWWVzIiksIGxhYmVscyA9IGMoMCwgMSkpDQpIb2JieV9EYXRhJFdvbl9hcnRzID0gZmFjdG9yKEhvYmJ5X0RhdGEkV29uX2FydHMsIGxldmVscyA9IGMoIk5vIiwgIk1heWJlIiwgIlllcyIpLCBsYWJlbHMgPSBjKDAsIDIsIDEpKQ0KSG9iYnlfRGF0YSRGYXZfc3ViID0gZmFjdG9yKEhvYmJ5X0RhdGEkRmF2X3N1YiwgbGV2ZWxzID0gYygiU2NpZW5jZSIsICJNYXRoZW1hdGljcyIsICJIaXN0b3J5L0dlb2dyYXBoeSIsICJBbnkgbGFuZ3VhZ2UiKSwgbGFiZWxzID0gYygxLCAyLCAzLCA0KSkNCkhvYmJ5X0RhdGEkYFByZWRpY3RlZCBIb2JieWAgPC0gZmFjdG9yKEhvYmJ5X0RhdGEkYFByZWRpY3RlZCBIb2JieWAsIGxldmVscyA9IGMoIkFjYWRlbWljcyIsICJBcnRzIiwgIlNwb3J0cyIpLCBsYWJlbHMgPSBjKDEsIDIsIDMpKQ0KYGBgDQoNCkRhdGFzZXQgYWZ0ZXIgRW5jb2RpbmcgOg0KYGBge3J9DQpzYW1wbGUoSG9iYnlfRGF0YSkNCmBgYA0KDQoNCioqIzMjIE5vcm1hbGl6YXRpb24gYW5kIERpc2NldGl6YXRpb246KioNCg0KV2UgZG9uJ3QgbmVlZCB0byB1c2Ugbm9ybWFsaXphdGlvbiBhbmQgZGlzY2V0aXphdGlvbiBpbiBvdXIgZGF0YXNldC4gU2luY2Ugb3VyIGRhdGFzZXQgZG9lc24ndCBoYXZlIG51bWVyaWMgYXR0cmlidXRlcyBhbmQgbm9ybWFsaXphdGlvbiBpbnZvbHZlcyBtYXRoZW1hdGljYWwgb3BlcmF0aW9ucywgd2hpY2ggY2FuIHJlc3VsdCBpbiBtZWFuaW5nbGVzcyB2YWx1ZXMgYW5kIGVycm9ycywgQWxzbywgYXBwbHlpbmcgZGlzY3JldGl6YXRpb24gbGVhZHMgdG8gYSBsb3NzIG9mIGluZm9ybWF0aW9uIGFuZCBjcmVhdGVzIGludGVydmFscyBhbmQgcmVsYXRpb25zaGlwcyB0aGF0IGRvbid0IGV4aXN0IGJldHdlZW4gdmFsdWVzLg0KDQoqKiM0I0ZlYXR1cmUgU2VsZWN0aW9uOioqDQoNClRvIGltcHJvdmUgdGhlIGFjY3VyYWN5IG9mIG91ciBwcmVkaWN0aW9ucyBmb3IgdGhlIHRhcmdldCBjbGFzcyAiUHJlZGljdGVkIEhvYmJ5IiBhbmQgZGVjcmVhc2UgdGhlIHByb2Nlc3NpbmcgdGltZSBvZiBvdXIgY2xhc3NpZmllciwgd2Ugd2lsbCB1dGlsaXplIGZlYXR1cmUgc2VsZWN0aW9uIHRlY2huaXF1ZXMuIFRoZXNlIHRlY2huaXF1ZXMgZW5hYmxlIHVzIHRvIGVsaW1pbmF0ZSByZWR1bmRhbnQgb3IgaXJyZWxldmFudCBhdHRyaWJ1dGVzIGZyb20gdGhlIGRhdGFzZXQsIHJlc3VsdGluZyBpbiBhIG1vcmUgY29uY2lzZSBzdWJzZXQgb2YgZmVhdHVyZXMgdGhhdCBwcm92aWRlIHRoZSBtb3N0IHZhbHVhYmxlIGluZm9ybWF0aW9uIGZvciBvdXIgcHJlZGljdGlvbnMuDQoNClNwZWNpZmljYWxseSwgd2Ugd2lsbCB1c2UgdHdvIGZlYXR1cmUgc2VsZWN0aW9uIG1ldGhvZHM6IFJhbmsgRmVhdHVyZXMgYnkgSW1wb3J0YW5jZSBhbmQgRmVhdHVyZSBTZWxlY3Rpb24gVXNpbmcgUmVjdXJzaXZlIEZlYXR1cmUgRWxpbWluYXRpb24gKFJGRSkuDQoNCjEuKipSYW5rIEZlYXR1cmVzIGJ5IEltcG9ydGFuY2U6KioNCg0KVGhpcyBtZXRob2QgdXNlZCBpbiB0aGUgY29kZSBoZWxwcyB1cyBkZXRlcm1pbmUgd2hpY2ggZmVhdHVyZXMgYXJlIG1vc3QgaW1wb3J0YW50IGZvciBwcmVkaWN0aW5nIHRoZSAiUHJlZGljdGVkIEhvYmJ5IiBjbGFzcyBsYWJlbCBpbiB0aGUgZGF0YXNldC4gSXQgdXRpbGl6ZXMgdGhlIFJhbmRvbSBGb3Jlc3QgYWxnb3JpdGhtLCBrbm93biBmb3IgaXRzIGFjY3VyYXRlIHByZWRpY3Rpb24gY2FwYWJpbGl0aWVzLiBUaGlzIG1ldGhvZCBjYWxjdWxhdGVzIHRoZSBpbXBvcnRhbmNlIG9mIGVhY2ggZmVhdHVyZSBieSBhc3Nlc3NpbmcgaXRzIGNvbnRyaWJ1dGlvbiB0byB0aGUgb3ZlcmFsbCBhY2N1cmFjeSBvZiB0aGUgcHJlZGljdGlvbnMuIEJ5IHJhbmtpbmcgdGhlIGZlYXR1cmVzIGJhc2VkIG9uIHRoZWlyIGltcG9ydGFuY2UsIHdlIGNhbiBpZGVudGlmeSB0aGUgb25lcyB0aGF0IGhhdmUgdGhlIGdyZWF0ZXN0IGluZmx1ZW5jZSBvbiBkZXRlcm1pbmluZyBob2JiaWVzLg0KDQpFbnN1cmUgdGhlIHJlc3VsdHMgYXJlIHJlcGVhdGFibGUgYnkgc2V0dGluZyBhIHNlZWQ6DQoNCmBgYHtyfQ0Kc2V0LnNlZWQoNykNCg0KYGBgDQoNCkxvYWQgdGhlIG5lY2Vzc2FyeSBsaWJyYXJpZXM6DQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQ0KaW5zdGFsbC5wYWNrYWdlcygicmFuZG9tRm9yZXN0IikNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkNCmBgYA0KDQpTZXBhcmF0ZSB0aGUgcHJlZGljdG9ycyBhbmQgdGhlIGNsYXNzIGxhYmVsOg0KDQpgYGB7cn0NCnByZWRpY3RvcnMgPC0gSG9iYnlfRGF0YVssIC0xNF0gICMgRXhjbHVkaW5nIHRoZSBjbGFzcyBsYWJlbCAoUHJlZGljdGVkIEhvYmJ5KQ0KY2xhc3NfbGFiZWwgPC0gSG9iYnlfRGF0YSRgUHJlZGljdGVkIEhvYmJ5YA0KYGBgDQoNClRyYWluIGEgUmFuZG9tIEZvcmVzdCBtb2RlbDoNCg0KYGBge3J9DQptb2RlbCA8LSByYW5kb21Gb3Jlc3QocHJlZGljdG9ycywgY2xhc3NfbGFiZWwsIGltcG9ydGFuY2UgPSBUUlVFKQ0KYGBgDQoNCkdldCB0aGUgdmFyaWFibGUgaW1wb3J0YW5jZToNCg0KYGBge3J9DQppbXBvcnRhbmNlIDwtIGltcG9ydGFuY2UobW9kZWwpDQpgYGANCg0KUmFuayB0aGUgZmVhdHVyZXMgYnkgaW1wb3J0YW5jZToNCg0KYGBge3J9DQpyYW5rZWRfZmVhdHVyZXMgPC0gc29ydChpbXBvcnRhbmNlWywgIk1lYW5EZWNyZWFzZUdpbmkiXSwgZGVjcmVhc2luZyA9IFRSVUUpDQpgYGANCg0KUHJpbnQgdGhlIHJhbmtlZCBmZWF0dXJlczoNCg0KYGBge3J9DQpwcmludChyYW5rZWRfZmVhdHVyZXMpDQoNCmJhcnBsb3QocmFua2VkX2ZlYXR1cmVzLCBob3JpeiA9IFRSVUUsIGxhcyA9IDEsIG1haW4gPSAiS2lkcyBIb2JieSBWYXJpYWJsZSBJbXBvcnRhbmNlIFJhbmtpbmciKQ0KYGBgDQoNCjIuKipGZWF0dXJlIFNlbGVjdGlvbiBVc2luZyBSRkU6KioNCg0KVGhlIFJlY3Vyc2l2ZSBGZWF0dXJlIEVsaW1pbmF0aW9uIChSRkUpIG1ldGhvZCB3aXRoIFJhbmRvbSBGb3Jlc3QgaXMgYSB0ZWNobmlxdWUgdXNlZCB0byBzZWxlY3QgdGhlIG1vc3QgaW1wb3J0YW50IGZlYXR1cmVzIGZvciBhY2N1cmF0ZSBwcmVkaWN0aW9ucyAuIEl0IGl0ZXJhdGl2ZWx5IGVsaW1pbmF0ZXMgbGVzcyByZWxldmFudCBmZWF0dXJlcywgcmV0cmFpbmluZyB0aGUgbW9kZWwgYXQgZWFjaCBzdGVwIHRvIGV2YWx1YXRlIHBlcmZvcm1hbmNlLiBCeSBmb2N1c2luZyBvbiB0aGUgbW9zdCBpbmZvcm1hdGl2ZSBmZWF0dXJlcywgUkZFIGltcHJvdmVzIHJlZHVjZXMgY29tcGxleGl0eSwgYW5kIGVuaGFuY2VzIHByZWRpY3Rpb24gYWNjdXJhY3kuIEl0IGlzIHBhcnRpY3VsYXJseSBlZmZlY3RpdmUgd2l0aCBSYW5kb20gRm9yZXN0IGR1ZSB0byBpdHMgYWJpbGl0eSB0byBoYW5kbGUgY29tcGxleCByZWxhdGlvbnNoaXBzIGFuZCBoaWdoLWRpbWVuc2lvbmFsIGRhdGEuIFJGRSBoZWxwcyBpZGVudGlmeSB0aGUgbW9zdCBpbXBvcnRhbnQgYXR0cmlidXRlcyBhc3NvY2lhdGVkIHdpdGggdGhlIHRhcmdldCB2YXJpYWJsZSwgZW5hYmxpbmcgdGhlIGNyZWF0aW9uIG9mIG1vcmUgZWZmaWNpZW50IGFuZCBhY2N1cmF0ZSBtb2RlbHMuDQoNCkVuc3VyZSB0aGUgcmVzdWx0cyBhcmUgcmVwZWF0YWJsZSBieSBzZXR0aW5nIGEgc2VlZDoNCg0KYGBge3J9DQpzZXQuc2VlZCg3KQ0KYGBgDQoNCkxvYWQgdGhlIG5lY2Vzc2FyeSBsaWJyYXJpZXM6DQoNCmBgYHtyfQ0KbGlicmFyeShjYXJldCkNCmBgYA0KDQpEZWZpbmUgdGhlIGNvbnRyb2wgcGFyYW1ldGVycyBmb3IgUkZFIHVzaW5nIHJhbmRvbSBmb3Jlc3Qgc2VsZWN0aW9uIGZ1bmN0aW9uOg0KDQpgYGB7cn0NCmNvbnRyb2wgPC0gcmZlQ29udHJvbChmdW5jdGlvbnMgPSByZkZ1bmNzLCBtZXRob2QgPSAiY3YiLCBudW1iZXIgPSAxMCkNCmBgYA0KDQpFeHRyYWN0IHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzIGZyb20gSG9iYnlfRGF0YToNCg0KYGBge3J9DQpwcmVkaWN0b3JzIDwtIEhvYmJ5X0RhdGFbLCAtbmNvbChIb2JieV9EYXRhKV0NCmBgYA0KDQpDb252ZXJ0IHRoZSBvdXRjb21lIHZhcmlhYmxlIHRvIGEgZmFjdG9yOg0KDQpgYGB7cn0NCm91dGNvbWUgPC0gYXMuZmFjdG9yKEhvYmJ5X0RhdGEkYFByZWRpY3RlZCBIb2JieWApDQpgYGANCg0KUnVuIHRoZSBSRkUgYWxnb3JpdGhtOg0KDQpgYGB7cn0NCnJlc3VsdHMgPC0gcmZlKHByZWRpY3RvcnMsIG91dGNvbWUsIHNpemVzID0gMTpuY29sKEhvYmJ5X0RhdGEpLCByZmVDb250cm9sID0gY29udHJvbCkNCmBgYA0KDQpTdW1tYXJpemUgdGhlIHJlc3VsdHM6DQoNCmBgYHtyfQ0KcHJpbnQocmVzdWx0cykNCmBgYA0KDQpMaXN0IHRoZSBjaG9zZW4gZmVhdHVyZXMgc2VsZWN0ZWQgYnkgUkZFOg0KDQpgYGB7cn0NCnByZWRpY3RvcnMocmVzdWx0cykNCmBgYA0KDQpQbG90IHRoZSByZXN1bHRzOg0KDQpgYGB7cn0NCnBsb3QocmVzdWx0cywgdHlwZSA9IGMoImciLCAibyIpKQ0KYGBgDQoNCjMuKipSZW1vdmluZyBJcnJlbGV2YW50IENvbHVtbnM6KioNCg0KQnkgY29uc2lkZXJpbmcgYm90aCBSZWN1cnNpdmUgRmVhdHVyZSBFbGltaW5hdGlvbiAoUkZFKSBhbmQgUmFuayBCeSBJbXBvcnRhbmNlLCB3ZSBjYW4gbWFrZSBpbmZvcm1lZCBkZWNpc2lvbnMgYWJvdXQgZmVhdHVyZSByZWxldmFuY2UgYW5kIGltcGFjdCBvbiB0aGUgbW9kZWwuIEluIHRoaXMgY2FzZSwgdGhlIGNvbHVtbnMgIlNjaG9vbCwiICJNZWRhbHMiIHNob3VsZCBiZSBkZWxldGVkIGFzIHRoZXkgaGF2ZSBsb3dlciBpbXBvcnRhbmNlIHNjb3JlcyBjb21wYXJlZCB0byB0aGUgc2VsZWN0ZWQgdmFyaWFibGVzLiBSZW1vdmluZyB0aGVzZSBjb2x1bW5zIHNpbXBsaWZpZXMgdGhlIG1vZGVsIGFuZCByZWR1Y2VzIGRpbWVuc2lvbmFsaXR5LCBlbGltaW5hdGluZyBwb3RlbnRpYWwgbm9pc2UgYW5kIGlycmVsZXZhbnQgaW5mb3JtYXRpb24gdGhhdCBjb3VsZCBoaW5kZXIgYWNjdXJhdGUgcHJlZGljdGlvbnMuDQoNClJlbW92ZSB0aGUgc3BlY2lmaWVkIGNvbHVtbnMgZnJvbSB0aGUgSG9iYnlfS2lkcyBkYXRhc2V0Og0KDQpgYGB7cn0NCkhvYmJ5X0RhdGEgPC0gSG9iYnlfRGF0YVssICEoY29sbmFtZXMoSG9iYnlfRGF0YSkgJWluJSBjKCJTY2hvb2wiLCAiTWVkYWxzIikpXQ0KYGBgDQoNCkRpc3BsYXkgdGhlIHVwZGF0ZWQgZGF0YXNldCBhZnRlciBkZWxldGluZyBjb2x1bW5zOg0KDQpgYGB7cn0NCnNhbXBsZShIb2JieV9EYXRhKQ0KYGBgDQp3ZSBjYW4gc2VlIHRoYXQgdGhlIHR3byBjb2x1bW5zIGFyZSBkZWxldGVkKCJTY2hvb2wiLCAiTWVkYWxzIikuDQoNCg0KDQoNCioqQmFsYW5jZWQgRGF0YSoqDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIGNsYXNzIGltYmFsYW5jZQ0KY2xhc3NfaW1iYWxhbmNlIDwtIG1heChwcm9wLnRhYmxlKHRhYmxlKEhvYmJ5X0RhdGEkYFByZWRpY3RlZCBIb2JieWApKSkgLSBtaW4ocHJvcC50YWJsZSh0YWJsZShIb2JieV9EYXRhJGBQcmVkaWN0ZWQgSG9iYnlgKSkpDQoNCiMgUHJpbnQgdGhlIHJlc3VsdA0KcHJpbnQoY2xhc3NfaW1iYWxhbmNlKQ0KYGBgDQppZiBjbGFzc19pbWJhbGFuY2UgaXMgY2xvc2UgdG8gMCwgaXQgc3VnZ2VzdHMgdGhhdCB0aGUgcHJvcG9ydGlvbnMgb2YgZGlmZmVyZW50IGNsYXNzZXMgYXJlIHJlbGF0aXZlbHkgc2ltaWxhciwgaW5kaWNhdGluZyBhIGJhbGFuY2VkIGRhdGFzZXQuIENvbnZlcnNlbHksIGlmIGNsYXNzX2ltYmFsYW5jZSBpcyBsYXJnZXIsIGl0IHN1Z2dlc3RzIGEgbW9yZSBzaWduaWZpY2FudCBpbWJhbGFuY2UgYmV0d2VlbiB0aGUgY2xhc3Nlcy4NClRoZSBjYWxjdWxhdGVkIGNsYXNzIGltYmFsYW5jZSB2YWx1ZSBvZiAwLjE4MDUxMjIgc3VnZ2VzdHMgdGhhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGNsYXNzZXMgaW4gdGhlICJQcmVkaWN0ZWRfSG9iYnkiIGNvbHVtbiBvZiB0aGUgZGF0YXNldCBpcyByZWxhdGl2ZWx5IGJhbGFuY2VkLiBUaGUgY2xhc3MgaW1iYWxhbmNlIGlzIGEgbWVhc3VyZSBvZiB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBwcm9wb3J0aW9ucyBvZiB0aGUgbW9zdCBwcmV2YWxlbnQgYW5kIGxlYXN0IHByZXZhbGVudCBjbGFzc2VzLiBJbiB0aGlzIGNhc2UsIHRoZSB2YWx1ZSBpcyBjbG9zZSB0byAwLCBpbmRpY2F0aW5nIHRoYXQgdGhlcmUgaXMgYSBtaW5pbWFsIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcHJvcG9ydGlvbnMgb2YgZGlmZmVyZW50IGNsYXNzZXMuIEEgbG93ZXItY2xhc3MgaW1iYWxhbmNlIHZhbHVlIGlzIGdlbmVyYWxseSBkZXNpcmFibGUsIGFzIGl0IHNpZ25pZmllcyBhIG1vcmUgZXZlbiBkaXN0cmlidXRpb24gb2YgaW5zdGFuY2VzIGFjcm9zcyBjbGFzc2VzLCB3aGljaCBjYW4gYmUgYmVuZWZpY2lhbCBmb3IgdGhlIHRyYWluaW5nIGFuZCBwZXJmb3JtYW5jZSBvZiBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscy4NCg0KDQoqKkRhdGEgTWluaW5nIFRlY2huaXF1ZSoqDQoNCg0KKipFdmFsdWF0aW9uIEFuZCBDb21wYXJpc29uKioNCg0KKiotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gQ2xhc3NpZmljYXRpb24tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKioNCg0KQWZ0ZXIgcHJlcHJvY2Vzc2luZywgd2Ugd2lsbCBwcm9jZWVkIHRvIHRoZSBjbGFzc2lmaWNhdGlvbiBzdGVwLiBJbiB0aGlzIHBoYXNlLCBhcyBwYXJ0IG9mIHN1cGVydmlzZWQgbGVhcm5pbmcsIHdlIHdpbGwgYXBwbHkgYSBjbGFzc2lmaWNhdGlvbiBhbGdvcml0aG0gdG8gYXNzaWduIGVhY2ggZGF0YSBwb2ludCBpbnRvIHByZWRlZmluZWQgY2F0ZWdvcmllcyBiYXNlZCBvbiBpdHMgYXR0cmlidXRlcy4gVGhpcyBpbnZvbHZlcyBzZWxlY3RpbmcgdGhlIG1vc3QgcmVsZXZhbnQgZmVhdHVyZXMgdGhhdCBoYXZlIGJlZW4gY2xlYW5lZCBhbmQgZm9ybWF0dGVkIGR1cmluZyBwcmVwcm9jZXNzaW5nLiBUaGUgc2VsZWN0ZWQgbW9kZWwgd2lsbCB0aGVuIGxlYXJuIGZyb20gdHJhaW5pbmcgZGF0YSwgZW5hYmxpbmcgaXQgdG8gcHJlZGljdCB0aGUgY2F0ZWdvcnkgb2YgbmV3LCB1bnNlZW4gZGF0YSBhY2N1cmF0ZWx5LiBUaGlzIHN0ZXAgaXMgZXNzZW50aWFsIGZvciBtYWtpbmcgaW5mb3JtZWQgZGVjaXNpb25zIG9yIHByZWRpY3Rpb25zIGJhc2VkIG9uIHRoZSBkYXRhLg0KDQpXZSBpbXBsZW1lbnQgYSBkZWNpc2lvbiB0cmVlIG9uIHRoZSBkYXRhc2V0LCB3aGljaCBoYXMgYmVlbiBwYXJ0aXRpb25lZCBpbnRvIFRyYWluaW5nIGFuZCBUZXN0IHNldHMgdXNpbmcgdGhlIHBlcmNlbnRhZ2Ugc3BsaXQgbWV0aG9kLiBUaGlzIG1ldGhvZCBlbnN1cmVzIHRoYXQgZWFjaCBzdWJzZXQgaXMgYSByYW5kb21pemVkLCByZXByZXNlbnRhdGl2ZSBzYW1wbGUgb2YgdGhlIGVudGlyZSBkYXRhc2V0LCB0aHVzIG1pbmltaXppbmcgYmlhcyBhbmQgZW5hYmxpbmcgY29uc2lzdGVudCBtb2RlbCBwZXJmb3JtYW5jZSBldmFsdWF0aW9uLiBXZSBjaG9vc2UgdGhyZWUgZGlmZmVyZW50IHNwbGl0IHNpemVzOiAoIjkwJSIsICIxMCUiKSwgKCI4MCUiLCAiMjAlIiksIGFuZCAoIjcwJSIsICIzMCUiKS4gVGhlc2UgdmFyeWluZyBzaXplcyBhcmUgc2VsZWN0ZWQgdG8gcHJvdmlkZSBpbnNpZ2h0IGludG8gdGhlIG1vZGVsJ3MgcGVyZm9ybWFuY2Ugd2l0aCBkaWZmZXJlbnQgYW1vdW50cyBvZiBkYXRhLCB3aGljaCBpcyB2aXRhbCBmb3IgZGV0ZWN0aW5nIHVuaXF1ZSBwYXR0ZXJucyBhbmQgY29uZmlybWluZyB0aGUgbW9kZWwncyBjb25zaXN0ZW5jeSBpbiB2YXJpb3VzIHNpdHVhdGlvbnMuDQoNCkluIHRoZSBmaW5hbCBzdGVwcywgd2UgdXRpbGl6ZSBkYXRhIHZpc3VhbGl6YXRpb24gdG9vbHMgdG8gY3JlYXRlIHZpc3VhbCByZXByZXNlbnRhdGlvbnMgb2Ygb3VyIGRlY2lzaW9uIHRyZWVzLiBBZGRpdGlvbmFsbHksIHdlIGNvbmR1Y3QgYW4gZXhoYXVzdGl2ZSBldmFsdWF0aW9uIG9mIHRoZSBtb2RlbCwgZW1wbG95aW5nIGEgIkNvbmZ1c2lvbiBNYXRyaXgiIHRvIGlsbHVzdHJhdGUgdGhlIG91dGNvbWVzIGNsZWFybHkuDQoNCg0KDQoNCioqSW5mb3JtYXRpb24gR2FpbioqDQoNCkluZm9ybWF0aW9uIEdhaW4gaXMgcGFydGljdWxhcmx5IHVzZWZ1bCB3aGVuIGRlYWxpbmcgd2l0aCBjYXRlZ29yaWNhbCB0YXJnZXQgdmFyaWFibGVzIHVzaW5nIEluZm9ybWF0aW9uIEdhaW4gZm9yIHRoZSBpbml0aWFsIHBhcnRpdGlvbmluZyBvZiBhIGRhdGFzZXQgd2hlbiBidWlsZGluZyBhIGRlY2lzaW9uIHRyZWUgSXQncyBiYXNlZCBvbiB0aGUgY29uY2VwdCBvZiBlbnRyb3B5IGFuZCBhaW1zIHRvIG1heGltaXplIHRoZSBob21vZ2VuZWl0eSBvZiBzdWJzZXRzIGFmdGVyIGVhY2ggc3BsaXQuIFRoaXMgYXBwcm9hY2ggaGVscHMgY3JlYXRlIGFuIGVmZmVjdGl2ZSBkZWNpc2lvbiB0cmVlIGJ5IHNlbGVjdGluZyBmZWF0dXJlcyB0aGF0IHByb3ZpZGUgdGhlIG1vc3QgaW5mb3JtYXRpb24gZm9yIHByZWRpY3RpbmcgdGhlIHRhcmdldCB2YXJpYWJsZS4NCg0KDQoqKjEtSW5mb3JtYXRpb24gR2Fpbig3MCUsMzAlKSoqDQoNCmBgYHtyfQ0KIyBMb2FkIHRoZSByZXF1aXJlZCBwYWNrYWdlcw0KbGlicmFyeShycGFydCkNCmxpYnJhcnkocnBhcnQucGxvdCkNCmxpYnJhcnkoY2FyZXQpDQoNCiMgU2V0IHRoZSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCnNldC5zZWVkKDEyMzQpDQoNCiMgU3BsaXQgdGhlIGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzDQppbmQgPC0gc2FtcGxlKDIsIG5yb3coSG9iYnlfRGF0YSksIHJlcGxhY2U9VFJVRSwgcHJvYj1jKDAuNywgMC4zKSkNCnRyYWluRGF0YSA8LSBIb2JieV9EYXRhW2luZCA9PSAxLF0NCnRlc3REYXRhIDwtIEhvYmJ5X0RhdGFbaW5kID09IDIsXQ0KDQojIERlZmluZSB0aGUgZm9ybXVsYSBmb3IgdGhlIGRlY2lzaW9uIHRyZWUNCm15Rm9ybXVsYSA8LSBgUHJlZGljdGVkIEhvYmJ5YCB+IFNjaG9sYXJzaGlwICsgRmF2X3N1YiArIFByb2plY3RzICsgR3Jhc3BfcG93ICsgVGltZV9zcHJ0ICsgQ2FyZWVyX3NwcnQgKyBBY3Rfc3BydCArIEZhbnRfYXJ0cyArIFdvbl9hcnRzICsgVGltZV9hcnQgKyBPbHltcGlhZF9QYXJ0aWNpcGF0aW9uDQoNCiMgQ3JlYXRlIHRoZSBkZWNpc2lvbiB0cmVlIG1vZGVsIHdpdGggdGhlICJpbmZvcm1hdGlvbiIgc3BsaXR0aW5nIGNyaXRlcmlvbg0KSG9iYnlfRGF0YV9jdHJlZSA8LSBycGFydChteUZvcm11bGEsIGRhdGEgPSB0cmFpbkRhdGEsIG1ldGhvZCA9ICJjbGFzcyIsIHBhcm1zID0gbGlzdChzcGxpdCA9ICJpbmZvcm1hdGlvbiIpKQ0KDQoNCg0KDQoNCiMgUHJpbnQgdGhlIGRlY2lzaW9uIHRyZWUNCnByaW50KEhvYmJ5X0RhdGFfY3RyZWUpDQoNCiMgUGxvdCB0aGUgZGVjaXNpb24gdHJlZQ0KDQpycGFydC5wbG90KEhvYmJ5X0RhdGFfY3RyZWUpDQoNCg0KDQoNCmBgYA0KDQoqKkRlY2lzaW9uIFRyZWUgQW5hbHlzaXMgVXNpbmcgSW5mb3JtYXRpb24gZ2Fpbig3MC8zMCk6KioNCg0KSW4gRnJpc3QgVHJlZSAsd2UgZGV2aWRlIGRhdGFzZXQgaW50byB0cmFpbmluZyBzZXQgYW5kIHRlc3Qgc2V0IHdpdGggc2l6ZSglNzAsJTMwKSByZXNwZWN0aXZlbHkuIEFzIHlvdSBjYW4gc2VlIGluIHRoZSBmaWd1cmUsIHRoZSByb290IG5vZGUgKCJDYXJlZXJfc3BydCIpIHNlcnZlcyBhcyB0aGUgc3RhcnRpbmcgcG9pbnQgZm9yIHRoZSBjbGFzc2lmaWNhdGlvbiBwcm9jZXNzIHNpbmNlIGhhdmUgdGhlIGhlaWdodHMgR2Fpbi4gVGhlIGRhdGFzZXQgaGFzIGEgZGlzdHJpYnV0aW9uIG9mIGFwcHJveGltYXRlbHkgNDIuNDclIGZvciBjbGFzcyAxKCJBY2FkZW1pY3MiKSwgMjUuOTIlIGZvciBjbGFzcyAyKCJBcnRzIiksIGFuZCAzMS42MSUgZm9yIGNsYXNzIDMgKCJTcG9ydHMiKS4NCg0KVGhlIHRyZWUgZnVydGhlciBicmFuY2hlcyBiYXNlZCBvbiB0aGUgdmFsdWVzIG9mIHRoZSAiQ2FyZWVyX3NwcnQiIGlmIGVxdWFsIDAsIHRyZWUgYnJhbmNoZXMgYmFzZWQgb24iV29uX2FydHMiICxhbmQgbWFqb3JpdHkgb2YgaW5zdGFuY2VzIGZhbGwgaW50byAiQWNhZGltY3MiLCBjb25zdGl0dXRpbmcgNjMuOTMlICwgaWYiV29uX2FydHMiIGVxdWFsIDAgb3IyIEluIHRoaXMgY2FzZSB0aGUgdHJlZSB0ZXJtaW5hdGVzIHdpdGggYSBsZWFmIG5vZGUgaW5kaWNhdGluZyBhIGhpZ2ggcHJvYmFiaWxpdHkgODkuMDglIGZvciAiQWNhZGltY3MiLmVsc2UgdGhlIGluc3RhbmNlcyBhcmUgY2xhc3NpZmllZCBiYXNlZCBvbiAiRmFudF9hcnRzIiBpbnRvICJBcnRzIiB3aXRoIGEgcHJvYmFiaWxpdHkgODglLGlmIGVxdWFsIDAgdGhlIGluc3RhbmNlcyBhcmUgY2xhc3NpZmllZCBpbnRvICJBY2FkZW1pY3MiIHdpdGggYSBwcm9iYWJpbGl0eSBvZiA3MyUgLGVsc2UgdGhlIGluc3RhbmNlcyBhcmUgY2xhc3NpZmllZCBpbnRvICJBcnRzIiB3aXRoIGEgaGlnaCBwcm9iYWJpbGl0eSA5NiUsIGlmICJDYXJlZXJfc3BydCIgaXMgMSwgdGhlIHRyZWUgZnVydGhlciBicmFuY2hlcyBiYXNlZCBvbiB0aGUgIkZhbnRfYXJ0cyIgaW50byAiU3BvcnRzIiB3aXRoIGEgcHJvYmFiaWxpdHkgODAlLiBJZiAiRmFudF9hcnRzIiBpcyAxICwgdGhlcmUgaXMgYW5vdGhlciBzcGxpdCBiYXNlZCBvbiB0aGUgIlRpbWVfYXJ0IiBmZWF0dXJlIGludG8gIkFydHMiIHdpdGggYSBwcm9iYWJpbGl0eSA0OSUuIElmICJUaW1lX2FydCIgaXMgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIDMsIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgaW50byAiQXJ0cyIgd2l0aCBhIHByb2JhYmlsaXR5IG9mIDg3JSAuIE9uIHRoZSBvdGhlciBoYW5kLCBpZiAiVGltZV9hcnQiIGlzIGxlc3MgdGhhbiAzLCB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGludG8gIlNwb3J0cyIgd2l0aCBhIHByb2JhYmlsaXR5IG9mIDY1JSAuSWYgIkZhbnRfYXJ0cyIgbm90IGVxdWFsIDEgLCB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGludG8gIlNwb3J0cyIgd2l0aCBhIGhpZ2ggcHJvYmFiaWxpdHkgOTUlLCBhcyBpbmRpY2F0ZWQgYnkgdGhlIGxlYWYgbm9kZS4NCg0KKipGaXJzdCBDb25mdXNpb24gbWF0cml4KioNCg0KYGBge3J9DQojIFByZWRpY3Qgb24gdGhlIHRlc3QgZGF0YQ0KICAgDQp0ZXN0UHJlZCA8LSBwcmVkaWN0KEhvYmJ5X0RhdGFfY3RyZWUsIG5ld2RhdGEgPSB0ZXN0RGF0YSx0eXBlID0gJ2NsYXNzJyApDQoNCg0KIyBDaGVjayB0aGUgYWNjdXJhY3kgb2YgdGhlIG1vZGVsDQphY2N1cmFjeSA8LSBzdW0odGVzdFByZWQgPT0gdGVzdERhdGEkYFByZWRpY3RlZCBIb2JieWApIC8gbnJvdyh0ZXN0RGF0YSkgKiAxMDANCmFjY3VyYWN5DQoNCiMgQ2FsY3VsYXRlIHRoZSBjb25mdXNpb24gbWF0cml4IGFuZCBkaXNwbGF5IHJlc3VsdHMNCnJlc3VsdHMgPC0gY29uZnVzaW9uTWF0cml4KHRlc3RQcmVkLCB0ZXN0RGF0YSRgUHJlZGljdGVkIEhvYmJ5YCkNCnByaW50KHJlc3VsdHMpDQoNCg0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBEZWZpbmUgaW5kaXZpZHVhbCBjbGFzcyB2YWx1ZXMgZm9yIGVhY2ggbWV0cmljDQpzZW5zaXRpdml0eV9jbGFzc18xIDwtIDAuOTI0MQ0Kc2Vuc2l0aXZpdHlfY2xhc3NfMiA8LSAwLjg2NTQNCnNlbnNpdGl2aXR5X2NsYXNzXzMgPC0gMC43ODA1DQoNCnNwZWNpZmljaXR5X2NsYXNzXzEgPC0gMC44NjAyDQpzcGVjaWZpY2l0eV9jbGFzc18yIDwtIDAuOTc1MA0Kc3BlY2lmaWNpdHlfY2xhc3NfMyA8LSAwLjk1NDINCg0KcG9zX3ByZWRfdmFsdWVfY2xhc3NfMSA8LSAwLjg0ODgNCnBvc19wcmVkX3ZhbHVlX2NsYXNzXzIgPC0gMC45Mzc1DQpwb3NfcHJlZF92YWx1ZV9jbGFzc18zIDwtIDAuODQyMQ0KDQpuZWdfcHJlZF92YWx1ZV9jbGFzc18xIDwtIDAuOTMwMg0KbmVnX3ByZWRfdmFsdWVfY2xhc3NfMiA8LSAwLjk0MzUNCm5lZ19wcmVkX3ZhbHVlX2NsYXNzXzMgPC0gMC45MzI4DQoNCnByZXZhbGVuY2VfY2xhc3NfMSA8LSAwLjQ1OTMNCnByZXZhbGVuY2VfY2xhc3NfMiA8LSAwLjMwMjMNCnByZXZhbGVuY2VfY2xhc3NfMyA8LSAwLjIzODQNCg0KZGV0ZWN0aW9uX3JhdGVfY2xhc3NfMSA8LSAwLjQyNDQNCmRldGVjdGlvbl9yYXRlX2NsYXNzXzIgPC0gMC4yNjE2DQpkZXRlY3Rpb25fcmF0ZV9jbGFzc18zIDwtIDAuMTg2MA0KDQpiYWxhbmNlZF9hY2N1cmFjeV9jbGFzc18xIDwtIDAuODkyMQ0KYmFsYW5jZWRfYWNjdXJhY3lfY2xhc3NfMiA8LSAwLjkyMDINCmJhbGFuY2VkX2FjY3VyYWN5X2NsYXNzXzMgPC0gMC44NjczDQoNCiMgQ2FsY3VsYXRlIG1hY3JvLWF2ZXJhZ2VzIGZvciBlYWNoIG1ldHJpYw0KbWFjcm9fYXZnX3NlbnNpdGl2aXR5IDwtIChzZW5zaXRpdml0eV9jbGFzc18xICsgc2Vuc2l0aXZpdHlfY2xhc3NfMiArIHNlbnNpdGl2aXR5X2NsYXNzXzMpIC8gMw0KbWFjcm9fYXZnX3NwZWNpZmljaXR5IDwtIChzcGVjaWZpY2l0eV9jbGFzc18xICsgc3BlY2lmaWNpdHlfY2xhc3NfMiArIHNwZWNpZmljaXR5X2NsYXNzXzMpIC8gMw0KbWFjcm9fYXZnX3Bvc19wcmVkX3ZhbHVlIDwtIChwb3NfcHJlZF92YWx1ZV9jbGFzc18xICsgcG9zX3ByZWRfdmFsdWVfY2xhc3NfMiArIHBvc19wcmVkX3ZhbHVlX2NsYXNzXzMpIC8gMw0KbWFjcm9fYXZnX25lZ19wcmVkX3ZhbHVlIDwtIChuZWdfcHJlZF92YWx1ZV9jbGFzc18xICsgbmVnX3ByZWRfdmFsdWVfY2xhc3NfMiArIG5lZ19wcmVkX3ZhbHVlX2NsYXNzXzMpIC8gMw0KbWFjcm9fYXZnX3ByZXZhbGVuY2UgPC0gKHByZXZhbGVuY2VfY2xhc3NfMSArIHByZXZhbGVuY2VfY2xhc3NfMiArIHByZXZhbGVuY2VfY2xhc3NfMykgLyAzDQptYWNyb19hdmdfZGV0ZWN0aW9uX3JhdGUgPC0gKGRldGVjdGlvbl9yYXRlX2NsYXNzXzEgKyBkZXRlY3Rpb25fcmF0ZV9jbGFzc18yICsgZGV0ZWN0aW9uX3JhdGVfY2xhc3NfMykgLyAzDQptYWNyb19hdmdfYmFsYW5jZWRfYWNjdXJhY3kgPC0gKGJhbGFuY2VkX2FjY3VyYWN5X2NsYXNzXzEgKyBiYWxhbmNlZF9hY2N1cmFjeV9jbGFzc18yICsgYmFsYW5jZWRfYWNjdXJhY3lfY2xhc3NfMykgLyAzDQoNCiMgUHJpbnQgdGhlIG1hY3JvLWF2ZXJhZ2VzIGZvciBlYWNoIG1ldHJpYw0KDQoNCg0KcHJpbnQocGFzdGUoJ0F2ZXJhZ2UgU2Vuc2l0aXZpdHkoUmVjYWxsKTonLCBtYWNyb19hdmdfc2Vuc2l0aXZpdHkpKQ0KcHJpbnQocGFzdGUoJ0F2ZXJhZ2UgU3BlY2lmaWNpdHk6JywgbWFjcm9fYXZnX3NwZWNpZmljaXR5KSkNCnByaW50KHBhc3RlKCdBdmVyYWdlIFBvcyBQcmVkIFZhbHVlOicsIG1hY3JvX2F2Z19wb3NfcHJlZF92YWx1ZSkpDQpwcmludChwYXN0ZSgnQXZlcmFnZSBOZWcgUHJlZCBWYWx1ZTonLCBtYWNyb19hdmdfbmVnX3ByZWRfdmFsdWUpKQ0KcHJpbnQocGFzdGUoJ0F2ZXJhZ2UgUHJldmFsZW5jZTonLCBtYWNyb19hdmdfcHJldmFsZW5jZSkpDQpwcmludChwYXN0ZSgnQXZlcmFnZSBEZXRlY3Rpb24gUmF0ZTonLCBtYWNyb19hdmdfZGV0ZWN0aW9uX3JhdGUpKQ0KcHJpbnQocGFzdGUoJ0F2ZXJhZ2UgQmFsYW5jZWQgQWNjdXJhY3k6JywgbWFjcm9fYXZnX2JhbGFuY2VkX2FjY3VyYWN5KSkNCg0KDQoNCg0KDQpgYGANCg0KDQoNCioqMi1JbmZvcm1hdGlvbiBHYWluKDgwJSwyMCUpKioNCg0KYGBge3J9DQpsaWJyYXJ5KHJwYXJ0KQ0KDQpsaWJyYXJ5KGNhVG9vbHMpDQpsaWJyYXJ5KHJwYXJ0LnBsb3QpDQpsaWJyYXJ5KGNhcmV0KQ0KDQojIFNldCB0aGUgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5DQpzZXQuc2VlZCgxMjM0KQ0KDQojIFNwbGl0IHRoZSBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cw0KaW5kIDwtIHNhbXBsZSgyLCBucm93KEhvYmJ5X0RhdGEpLCByZXBsYWNlPVRSVUUsIHByb2I9YygwLjgsIDAuMikpDQp0cmFpbkRhdGEgPC0gSG9iYnlfRGF0YVtpbmQgPT0gMSxdDQp0ZXN0RGF0YSA8LSBIb2JieV9EYXRhW2luZCA9PSAyLF0NCg0KIyBEZWZpbmUgdGhlIGZvcm11bGEgZm9yIHRoZSBkZWNpc2lvbiB0cmVlDQpteUZvcm11bGEgPC0gYFByZWRpY3RlZCBIb2JieWAgfiBTY2hvbGFyc2hpcCArIEZhdl9zdWIgKyBQcm9qZWN0cyArIEdyYXNwX3BvdyArIFRpbWVfc3BydCArIENhcmVlcl9zcHJ0ICsgQWN0X3NwcnQgKyBGYW50X2FydHMgKyBXb25fYXJ0cyArIFRpbWVfYXJ0ICsgT2x5bXBpYWRfUGFydGljaXBhdGlvbg0KDQojIENyZWF0ZSB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbCB3aXRoIHRoZSAiaW5mb3JtYXRpb24iIHNwbGl0dGluZyBjcml0ZXJpb24NCkhvYmJ5X0RhdGFfY3RyZWUgPC0gcnBhcnQobXlGb3JtdWxhLCBkYXRhID0gdHJhaW5EYXRhLCBtZXRob2QgPSAiY2xhc3MiLCBwYXJtcyA9IGxpc3Qoc3BsaXQgPSAiaW5mb3JtYXRpb24iKSkNCg0KDQoNCg0KDQojIFByaW50IHRoZSBkZWNpc2lvbiB0cmVlDQpwcmludChIb2JieV9EYXRhX2N0cmVlKQ0KDQojIFBsb3QgdGhlIGRlY2lzaW9uIHRyZWUNCg0KcnBhcnQucGxvdChIb2JieV9EYXRhX2N0cmVlKQ0KDQoNCg0KDQoNCg0KYGBgDQoNCioqRGVjaXNpb24gVHJlZSBBbmFseXNpcyBVc2luZyBJbmZvcm1hdGlvbiBnYWluKDgwLzIwKToqKg0KDQpJbiBTZWNvbmQgVHJlZSAsd2UgZGV2aWRlIGRhdGFzZXQgaW50byB0cmFpbmluZyBzZXQgYW5kIHRlc3Qgc2V0IHdpdGggc2l6ZSglODAsJTIwKSByZXNwZWN0aXZlbHkuIEFzIHlvdSBjYW4gc2VlIGluIHRoZSBmaWd1cmUsIHRoZSByb290IG5vZGUgKCJDYXJlZXJfc3BydCIpIHNlcnZlcyBhcyB0aGUgc3RhcnRpbmcgcG9pbnQgZm9yIHRoZSBjbGFzc2lmaWNhdGlvbiBwcm9jZXNzIHNpbmNlIGhhdmUgdGhlIGhlaWdodHMgR2Fpbi4gVGhlIGRhdGFzZXQgaGFzIGEgZGlzdHJpYnV0aW9uIG9mIGFwcHJveGltYXRlbHkgNDMlZm9yIGNsYXNzMSAoIkFjYWRlbWljcyIpLCAyNiUgZm9yIGNsYXNzIDIoIkFydHMiKSwgYW5kIDMxJSBmb3IgY2xhc3MgMyAoIlNwb3J0cyIpLg0KDQpUaGUgdHJlZSBmdXJ0aGVyIGJyYW5jaGVzIGJhc2VkIG9uIHRoZSB2YWx1ZXMgb2YgdGhlICJDYXJlZXJfc3BydCIgaWYgZXF1YWwgMCwgdHJlZSBicmFuY2hlcyBiYXNlZCBvbiJXb25fYXJ0cyIgLGFuZCBtYWpvcml0eSBvZiBpbnN0YW5jZXMgZmFsbCBpbnRvICJBY2FkaW1jcyIsIGNvbnN0aXR1dGluZyA2NSUgLCBpZiJXb25fYXJ0cyIgZXF1YWwgMCBvcjIgSW4gdGhpcyBjYXNlIHRoZSB0cmVlIHRlcm1pbmF0ZXMgd2l0aCBhIGxlYWYgbm9kZSBpbmRpY2F0aW5nIGEgaGlnaCBwcm9iYWJpbGl0eSA5MCUgZm9yICJBY2FkaW1jcyIuZWxzZSB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGJhc2VkIG9uICJGYW50X2FydHMiaW50byAiQXJ0cyIgd2l0aCBhIHByb2JhYmlsaXR5IDg4JSxpZiBlcXVhbCAwIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgaW50byAiQWNhZGVtaWNzIiB3aXRoIGEgcHJvYmFiaWxpdHkgb2YgNzIlICxlbHNlIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgaW50byAiQXJ0cyIgd2l0aCBhIGhpZ2ggcHJvYmFiaWxpdHkgOTYlLCBpZiAiQ2FyZWVyX3NwcnQiIGlzIDEsIHRoZSB0cmVlIGZ1cnRoZXIgYnJhbmNoZXMgYmFzZWQgb24gdGhlICJGYW50X2FydHMiIGludG8gIlNwb3J0cyIgd2l0aCBhIHByb2JhYmlsaXR5IDc4JS4gSWYgIkZhbnRfYXJ0cyIgaXMgMSAsIHRoZXJlIGlzIGFub3RoZXIgc3BsaXQgYmFzZWQgb24gdGhlICJUaW1lX2FydCIgaW50byAiQXJ0cyIgd2l0aCBhIHByb2JhYmlsaXR5IDUwJS4gSWYgIlRpbWVfYXJ0IiBpcyBncmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8gMywgdGhlIGluc3RhbmNlcyBhcmUgY2xhc3NpZmllZCBpbnRvICJBcnRzIiB3aXRoIGEgcHJvYmFiaWxpdHkgb2YgODUlIC4gT24gdGhlIG90aGVyIGhhbmQsIGlmICJUaW1lX2FydCIgaXMgbGVzcyB0aGFuIDMsIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgaW50byAiU3BvcnRzIiB3aXRoIGEgcHJvYmFiaWxpdHkgb2YgNTglIC5JZiAiRmFudF9hcnRzIiBJZiBub3QgZXF1YWwgMSAsIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgaW50byAiU3BvcnRzIiB3aXRoIGEgaGlnaCBwcm9iYWJpbGl0eSA5NCUsIGFzIGluZGljYXRlZCBieSB0aGUgbGVhZiBub2RlLg0KDQoqKlNlY29uZCBDb25mdXNpb24gbWF0cml4KioNCg0KYGBge3J9DQoNCg0KIyBQcmVkaWN0IG9uIHRoZSB0ZXN0IGRhdGENCiAgIA0KdGVzdFByZWQgPC0gcHJlZGljdChIb2JieV9EYXRhX2N0cmVlLCBuZXdkYXRhID0gdGVzdERhdGEsdHlwZSA9ICdjbGFzcycgKQ0KDQojIENoZWNrIHRoZSBhY2N1cmFjeSBvZiB0aGUgbW9kZWwNCmFjY3VyYWN5IDwtIHN1bSh0ZXN0UHJlZCA9PSB0ZXN0RGF0YSRgUHJlZGljdGVkIEhvYmJ5YCkgLyBucm93KHRlc3REYXRhKSAqIDEwMA0KYWNjdXJhY3kNCg0KIyBDYWxjdWxhdGUgdGhlIGNvbmZ1c2lvbiBtYXRyaXggYW5kIGRpc3BsYXkgcmVzdWx0cw0KcmVzdWx0cyA8LSBjb25mdXNpb25NYXRyaXgodGVzdFByZWQsIHRlc3REYXRhJGBQcmVkaWN0ZWQgSG9iYnlgKQ0KcHJpbnQocmVzdWx0cykNCg0KDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgRGVmaW5lIGluZGl2aWR1YWwgY2xhc3MgdmFsdWVzIGZvciBlYWNoIG1ldHJpYw0Kc2Vuc2l0aXZpdHlfY2xhc3NfMSA8LSAwLjkyNDENCnNlbnNpdGl2aXR5X2NsYXNzXzIgPC0gMC44NjU0DQpzZW5zaXRpdml0eV9jbGFzc18zIDwtIDAuNzgwNQ0KDQpzcGVjaWZpY2l0eV9jbGFzc18xIDwtIDAuODYwMg0Kc3BlY2lmaWNpdHlfY2xhc3NfMiA8LSAwLjk3NTANCnNwZWNpZmljaXR5X2NsYXNzXzMgPC0gMC45NTQyDQoNCnBvc19wcmVkX3ZhbHVlX2NsYXNzXzEgPC0gMC44NDg4DQpwb3NfcHJlZF92YWx1ZV9jbGFzc18yIDwtIDAuOTM3NQ0KcG9zX3ByZWRfdmFsdWVfY2xhc3NfMyA8LSAwLjg0MjENCg0KbmVnX3ByZWRfdmFsdWVfY2xhc3NfMSA8LSAwLjkzMDINCm5lZ19wcmVkX3ZhbHVlX2NsYXNzXzIgPC0gMC45NDM1DQpuZWdfcHJlZF92YWx1ZV9jbGFzc18zIDwtIDAuOTMyOA0KDQpwcmV2YWxlbmNlX2NsYXNzXzEgPC0gMC40NTkzDQpwcmV2YWxlbmNlX2NsYXNzXzIgPC0gMC4zMDIzDQpwcmV2YWxlbmNlX2NsYXNzXzMgPC0gMC4yMzg0DQoNCmRldGVjdGlvbl9yYXRlX2NsYXNzXzEgPC0gMC40MjQ0DQpkZXRlY3Rpb25fcmF0ZV9jbGFzc18yIDwtIDAuMjYxNg0KZGV0ZWN0aW9uX3JhdGVfY2xhc3NfMyA8LSAwLjE4NjANCg0KYmFsYW5jZWRfYWNjdXJhY3lfY2xhc3NfMSA8LSAwLjg5MjENCmJhbGFuY2VkX2FjY3VyYWN5X2NsYXNzXzIgPC0gMC45MjAyDQpiYWxhbmNlZF9hY2N1cmFjeV9jbGFzc18zIDwtIDAuODY3Mw0KDQojIENhbGN1bGF0ZSBtYWNyby1hdmVyYWdlcyBmb3IgZWFjaCBtZXRyaWMNCm1hY3JvX2F2Z19zZW5zaXRpdml0eSA8LSAoc2Vuc2l0aXZpdHlfY2xhc3NfMSArIHNlbnNpdGl2aXR5X2NsYXNzXzIgKyBzZW5zaXRpdml0eV9jbGFzc18zKSAvIDMNCm1hY3JvX2F2Z19zcGVjaWZpY2l0eSA8LSAoc3BlY2lmaWNpdHlfY2xhc3NfMSArIHNwZWNpZmljaXR5X2NsYXNzXzIgKyBzcGVjaWZpY2l0eV9jbGFzc18zKSAvIDMNCm1hY3JvX2F2Z19wb3NfcHJlZF92YWx1ZSA8LSAocG9zX3ByZWRfdmFsdWVfY2xhc3NfMSArIHBvc19wcmVkX3ZhbHVlX2NsYXNzXzIgKyBwb3NfcHJlZF92YWx1ZV9jbGFzc18zKSAvIDMNCm1hY3JvX2F2Z19uZWdfcHJlZF92YWx1ZSA8LSAobmVnX3ByZWRfdmFsdWVfY2xhc3NfMSArIG5lZ19wcmVkX3ZhbHVlX2NsYXNzXzIgKyBuZWdfcHJlZF92YWx1ZV9jbGFzc18zKSAvIDMNCm1hY3JvX2F2Z19wcmV2YWxlbmNlIDwtIChwcmV2YWxlbmNlX2NsYXNzXzEgKyBwcmV2YWxlbmNlX2NsYXNzXzIgKyBwcmV2YWxlbmNlX2NsYXNzXzMpIC8gMw0KbWFjcm9fYXZnX2RldGVjdGlvbl9yYXRlIDwtIChkZXRlY3Rpb25fcmF0ZV9jbGFzc18xICsgZGV0ZWN0aW9uX3JhdGVfY2xhc3NfMiArIGRldGVjdGlvbl9yYXRlX2NsYXNzXzMpIC8gMw0KbWFjcm9fYXZnX2JhbGFuY2VkX2FjY3VyYWN5IDwtIChiYWxhbmNlZF9hY2N1cmFjeV9jbGFzc18xICsgYmFsYW5jZWRfYWNjdXJhY3lfY2xhc3NfMiArIGJhbGFuY2VkX2FjY3VyYWN5X2NsYXNzXzMpIC8gMw0KDQojIFByaW50IHRoZSBtYWNyby1hdmVyYWdlcyBmb3IgZWFjaCBtZXRyaWMNCnByaW50KHBhc3RlKCdBdmVyYWdlIFNlbnNpdGl2aXR5KFJlY2FsbCk6JywgbWFjcm9fYXZnX3NlbnNpdGl2aXR5KSkNCnByaW50KHBhc3RlKCdBdmVyYWdlIFNwZWNpZmljaXR5OicsIG1hY3JvX2F2Z19zcGVjaWZpY2l0eSkpDQpwcmludChwYXN0ZSgnQXZlcmFnZSBQb3MgUHJlZCBWYWx1ZTonLCBtYWNyb19hdmdfcG9zX3ByZWRfdmFsdWUpKQ0KcHJpbnQocGFzdGUoJ0F2ZXJhZ2UgTmVnIFByZWQgVmFsdWU6JywgbWFjcm9fYXZnX25lZ19wcmVkX3ZhbHVlKSkNCnByaW50KHBhc3RlKCdBdmVyYWdlIFByZXZhbGVuY2U6JywgbWFjcm9fYXZnX3ByZXZhbGVuY2UpKQ0KcHJpbnQocGFzdGUoJ0F2ZXJhZ2UgRGV0ZWN0aW9uIFJhdGU6JywgbWFjcm9fYXZnX2RldGVjdGlvbl9yYXRlKSkNCnByaW50KHBhc3RlKCdBdmVyYWdlIEJhbGFuY2VkIEFjY3VyYWN5OicsIG1hY3JvX2F2Z19iYWxhbmNlZF9hY2N1cmFjeSkpDQoNCg0KDQoNCg0KDQoNCg0KDQpgYGANCg0KKiozLUluZm9ybWF0aW9uIEdhaW4oOTAlLDEwJSkqKg0KDQpgYGB7cn0NCmxpYnJhcnkocnBhcnQpDQoNCmxpYnJhcnkoY2FUb29scykNCmxpYnJhcnkocnBhcnQucGxvdCkNCmxpYnJhcnkoY2FyZXQpDQojIFNldCB0aGUgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5DQpzZXQuc2VlZCgxMjM0KQ0KDQojIFNwbGl0IHRoZSBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cw0KaW5kIDwtIHNhbXBsZSgyLCBucm93KEhvYmJ5X0RhdGEpLCByZXBsYWNlPVRSVUUsIHByb2I9YygwLjksIDAuMSkpDQp0cmFpbkRhdGEgPC0gSG9iYnlfRGF0YVtpbmQgPT0gMSxdDQp0ZXN0RGF0YSA8LSBIb2JieV9EYXRhW2luZCA9PSAyLF0NCg0KIyBEZWZpbmUgdGhlIGZvcm11bGEgZm9yIHRoZSBkZWNpc2lvbiB0cmVlDQpteUZvcm11bGEgPC0gYFByZWRpY3RlZCBIb2JieWAgfiBTY2hvbGFyc2hpcCArIEZhdl9zdWIgKyBQcm9qZWN0cyArIEdyYXNwX3BvdyArIFRpbWVfc3BydCArIENhcmVlcl9zcHJ0ICsgQWN0X3NwcnQgKyBGYW50X2FydHMgKyBXb25fYXJ0cyArIFRpbWVfYXJ0ICsgT2x5bXBpYWRfUGFydGljaXBhdGlvbg0KDQojIENyZWF0ZSB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbCB3aXRoIHRoZSAiaW5mb3JtYXRpb24iIHNwbGl0dGluZyBjcml0ZXJpb24NCkhvYmJ5X0RhdGFfY3RyZWUgPC0gcnBhcnQobXlGb3JtdWxhLCBkYXRhID0gdHJhaW5EYXRhLCBtZXRob2QgPSAiY2xhc3MiLCBwYXJtcyA9IGxpc3Qoc3BsaXQgPSAiaW5mb3JtYXRpb24iKSkNCg0KDQoNCiMgUHJpbnQgdGhlIGRlY2lzaW9uIHRyZWUNCnByaW50KEhvYmJ5X0RhdGFfY3RyZWUpDQoNCiMgUGxvdCB0aGUgZGVjaXNpb24gdHJlZQ0KDQpycGFydC5wbG90KEhvYmJ5X0RhdGFfY3RyZWUpDQoNCg0KDQoNCmBgYA0KDQoqKkRlY2lzaW9uIFRyZWUgQW5hbHlzaXMgVXNpbmcgSW5mb3JtYXRpb24gZ2Fpbig5MC8xMCk6KioNCg0KSW4gVGhpcmQgVHJlZSAsd2UgZGV2aWRlIGRhdGFzZXQgaW50byB0cmFpbmluZyBzZXQgYW5kIHRlc3Qgc2V0IHdpdGggc2l6ZSglOTAsJTEwKSByZXNwZWN0aXZlbHkuIEFzIHlvdSBjYW4gc2VlIGluIHRoZSBmaWd1cmUsIHRoZSByb290IG5vZGUgKCJDYXJlZXJfc3BydCIpIHNlcnZlcyBhcyB0aGUgc3RhcnRpbmcgcG9pbnQgZm9yIHRoZSBjbGFzc2lmaWNhdGlvbiBwcm9jZXNzIHNpbmNlIGhhdmUgdGhlIGhlaWdodHMgR2Fpbi4gVGhlIGRhdGFzZXQgaGFzIGEgZGlzdHJpYnV0aW9uIG9mIGFwcHJveGltYXRlbHkgNDMlIGZvciBjbGFzcyAxKCJBY2FkZW1pY3MiKSwgMjUlIGZvciBjbGFzcyAyKCJBcnRzIiksIGFuZCAzMiUgZm9yIGNsYXNzIDMgKCJTcG9ydHMiKS4NCg0KVGhlIHRyZWUgZnVydGhlciBicmFuY2hlcyBiYXNlZCBvbiB0aGUgdmFsdWVzIG9mIHRoZSAiQ2FyZWVyX3NwcnQiIGlmIGVxdWFsIDAsIHRyZWUgYnJhbmNoZXMgYmFzZWQgb24iV29uX2FydHMiICxhbmQgbWFqb3JpdHkgb2YgaW5zdGFuY2VzIGZhbGwgaW50byAiQWNhZGltY3MiLCBjb25zdGl0dXRpbmcgNjUlICwgaWYiV29uX2FydHMiIGVxdWFsIDAgb3IgMiBJbiB0aGlzIGNhc2UgdGhlIHRyZWUgdGVybWluYXRlcyB3aXRoIGEgbGVhZiBub2RlIGluZGljYXRpbmcgYSBoaWdoIHByb2JhYmlsaXR5IDkwJSBmb3IgIkFjYWRpbWNzIi5lbHNlIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgYmFzZWQgb24gIkZhbnRfYXJ0cyJpbnRvICJBcnRzIiB3aXRoIGEgcHJvYmFiaWxpdHkgODclLGlmIGVxdWFsIDAgdGhlIGluc3RhbmNlcyBhcmUgY2xhc3NpZmllZCBpbnRvICJBY2FkZW1pY3MiIHdpdGggYSBwcm9iYWJpbGl0eSBvZiA3NCUgLGVsc2UgdGhlIGluc3RhbmNlcyBhcmUgY2xhc3NpZmllZCBpbnRvICJBcnRzIiB3aXRoIGEgaGlnaCBwcm9iYWJpbGl0eSA5NiUsIGlmICJDYXJlZXJfc3BydCIgaXMgMSwgdGhlIHRyZWUgZnVydGhlciBicmFuY2hlcyBiYXNlZCBvbiB0aGUgIkZhbnRfYXJ0cyIgaW50byAiU3BvcnRzIiB3aXRoIGEgcHJvYmFiaWxpdHkgNzklLiBJZiAiRmFudF9hcnRzIiBpcyAxICwgdGhlcmUgaXMgYW5vdGhlciBzcGxpdCBiYXNlZCBvbiB0aGUgIlRpbWVfYXJ0IiBpbnRvICJBcnRzIiB3aXRoIGEgcHJvYmFiaWxpdHkgNDglLiBJZiAiVGltZV9hcnQiIGlzIGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byAzLCB0aGUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGludG8gIkFydHMiIHdpdGggYSBwcm9iYWJpbGl0eSBvZiA4NCUgLiBPbiB0aGUgb3RoZXIgaGFuZCwgaWYgIlRpbWVfYXJ0IiBpcyBsZXNzIHRoYW4gMywgdGhlIGluc3RhbmNlcyBhcmUgY2xhc3NpZmllZCBiYXNlZCBvbiJBY3Rfc3BydCAiIGludG8gIlNwb3J0cyIgd2l0aCBhIHByb2JhYmlsaXR5IG9mIDU4JSAuaWYgIkFjdF9zcHJ0IiBlcXVhbCAwIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgaW50byAiQWNhZGVtaWNzIiB3aXRoIGEgcHJvYmFiaWxpdHkgb2YgNTklLCBvdGhlciBoYW5kIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgaW50byAiU3BvcnRzIiB3aXRoIGEgcHJvYmFiaWxpdHkgb2YgNzglLiBJZiAiRmFudF9hcnRzIiBub3QgZXF1YWwgMSAsIHRoZSBpbnN0YW5jZXMgYXJlIGNsYXNzaWZpZWQgaW50byAiU3BvcnRzIiB3aXRoIGEgaGlnaCBwcm9iYWJpbGl0eSA5NCUsIGFzIGluZGljYXRlZCBieSB0aGUgbGVhZiBub2RlLg0KDQoqKlRoaXJkIENvbmZ1c2lvbiBtYXRyaXgqKg0KDQpgYGB7cn0NCg0KIyBQcmVkaWN0IG9uIHRoZSB0ZXN0IGRhdGENCiAgIA0KdGVzdFByZWQgPC0gcHJlZGljdChIb2JieV9EYXRhX2N0cmVlLCBuZXdkYXRhID0gdGVzdERhdGEsdHlwZSA9ICdjbGFzcycgKQ0KDQoNCiMgQ2hlY2sgdGhlIGFjY3VyYWN5IG9mIHRoZSBtb2RlbA0KYWNjdXJhY3kgPC0gc3VtKHRlc3RQcmVkID09IHRlc3REYXRhJGBQcmVkaWN0ZWQgSG9iYnlgKSAvIG5yb3codGVzdERhdGEpICogMTAwDQphY2N1cmFjeQ0KDQojIENhbGN1bGF0ZSB0aGUgY29uZnVzaW9uIG1hdHJpeCBhbmQgZGlzcGxheSByZXN1bHRzDQpyZXN1bHRzIDwtIGNvbmZ1c2lvbk1hdHJpeCh0ZXN0UHJlZCwgdGVzdERhdGEkYFByZWRpY3RlZCBIb2JieWApDQpwcmludChyZXN1bHRzKQ0KDQoNCg0KDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIERlZmluZSBpbmRpdmlkdWFsIGNsYXNzIHZhbHVlcyBmb3IgZWFjaCBtZXRyaWMNCnNlbnNpdGl2aXR5X2NsYXNzXzEgPC0gMC45MjQxDQpzZW5zaXRpdml0eV9jbGFzc18yIDwtIDAuODY1NA0Kc2Vuc2l0aXZpdHlfY2xhc3NfMyA8LSAwLjc4MDUNCg0Kc3BlY2lmaWNpdHlfY2xhc3NfMSA8LSAwLjg2MDINCnNwZWNpZmljaXR5X2NsYXNzXzIgPC0gMC45NzUwDQpzcGVjaWZpY2l0eV9jbGFzc18zIDwtIDAuOTU0Mg0KDQpwb3NfcHJlZF92YWx1ZV9jbGFzc18xIDwtIDAuODQ4OA0KcG9zX3ByZWRfdmFsdWVfY2xhc3NfMiA8LSAwLjkzNzUNCnBvc19wcmVkX3ZhbHVlX2NsYXNzXzMgPC0gMC44NDIxDQoNCm5lZ19wcmVkX3ZhbHVlX2NsYXNzXzEgPC0gMC45MzAyDQpuZWdfcHJlZF92YWx1ZV9jbGFzc18yIDwtIDAuOTQzNQ0KbmVnX3ByZWRfdmFsdWVfY2xhc3NfMyA8LSAwLjkzMjgNCg0KcHJldmFsZW5jZV9jbGFzc18xIDwtIDAuNDU5Mw0KcHJldmFsZW5jZV9jbGFzc18yIDwtIDAuMzAyMw0KcHJldmFsZW5jZV9jbGFzc18zIDwtIDAuMjM4NA0KDQpkZXRlY3Rpb25fcmF0ZV9jbGFzc18xIDwtIDAuNDI0NA0KZGV0ZWN0aW9uX3JhdGVfY2xhc3NfMiA8LSAwLjI2MTYNCmRldGVjdGlvbl9yYXRlX2NsYXNzXzMgPC0gMC4xODYwDQoNCmJhbGFuY2VkX2FjY3VyYWN5X2NsYXNzXzEgPC0gMC44OTIxDQpiYWxhbmNlZF9hY2N1cmFjeV9jbGFzc18yIDwtIDAuOTIwMg0KYmFsYW5jZWRfYWNjdXJhY3lfY2xhc3NfMyA8LSAwLjg2NzMNCg0KIyBDYWxjdWxhdGUgbWFjcm8tYXZlcmFnZXMgZm9yIGVhY2ggbWV0cmljDQoNCm1hY3JvX2F2Z19zZW5zaXRpdml0eSA8LSAoc2Vuc2l0aXZpdHlfY2xhc3NfMSArIHNlbnNpdGl2aXR5X2NsYXNzXzIgKyBzZW5zaXRpdml0eV9jbGFzc18zKSAvIDMNCg0KbWFjcm9fYXZnX3NwZWNpZmljaXR5IDwtIChzcGVjaWZpY2l0eV9jbGFzc18xICsgc3BlY2lmaWNpdHlfY2xhc3NfMiArIHNwZWNpZmljaXR5X2NsYXNzXzMpIC8gMw0KDQptYWNyb19hdmdfcG9zX3ByZWRfdmFsdWUgPC0gKHBvc19wcmVkX3ZhbHVlX2NsYXNzXzEgKyBwb3NfcHJlZF92YWx1ZV9jbGFzc18yICsgcG9zX3ByZWRfdmFsdWVfY2xhc3NfMykgLyAzDQoNCm1hY3JvX2F2Z19uZWdfcHJlZF92YWx1ZSA8LSAobmVnX3ByZWRfdmFsdWVfY2xhc3NfMSArIG5lZ19wcmVkX3ZhbHVlX2NsYXNzXzIgKyBuZWdfcHJlZF92YWx1ZV9jbGFzc18zKSAvIDMNCg0KbWFjcm9fYXZnX3ByZXZhbGVuY2UgPC0gKHByZXZhbGVuY2VfY2xhc3NfMSArIHByZXZhbGVuY2VfY2xhc3NfMiArIHByZXZhbGVuY2VfY2xhc3NfMykgLyAzDQoNCm1hY3JvX2F2Z19kZXRlY3Rpb25fcmF0ZSA8LSAoZGV0ZWN0aW9uX3JhdGVfY2xhc3NfMSArIGRldGVjdGlvbl9yYXRlX2NsYXNzXzIgKyBkZXRlY3Rpb25fcmF0ZV9jbGFzc18zKSAvIDMNCg0KbWFjcm9fYXZnX2JhbGFuY2VkX2FjY3VyYWN5IDwtIChiYWxhbmNlZF9hY2N1cmFjeV9jbGFzc18xICsgYmFsYW5jZWRfYWNjdXJhY3lfY2xhc3NfMiArIGJhbGFuY2VkX2FjY3VyYWN5X2NsYXNzXzMpIC8gMw0KDQoNCg0KDQojIFByaW50IHRoZSBtYWNyby1hdmVyYWdlcyBmb3IgZWFjaCBtZXRyaWMNCg0KDQoNCnByaW50KHBhc3RlKCdBdmVyYWdlIFNlbnNpdGl2aXR5KFJlY2FsbCk6JywgbWFjcm9fYXZnX3NlbnNpdGl2aXR5KSkNCnByaW50KHBhc3RlKCdBdmVyYWdlIFNwZWNpZmljaXR5OicsIG1hY3JvX2F2Z19zcGVjaWZpY2l0eSkpDQpwcmludChwYXN0ZSgnQXZlcmFnZSBQb3MgUHJlZCBWYWx1ZTonLCBtYWNyb19hdmdfcG9zX3ByZWRfdmFsdWUpKQ0KcHJpbnQocGFzdGUoJ0F2ZXJhZ2UgTmVnIFByZWQgVmFsdWU6JywgbWFjcm9fYXZnX25lZ19wcmVkX3ZhbHVlKSkNCnByaW50KHBhc3RlKCdBdmVyYWdlIFByZXZhbGVuY2U6JywgbWFjcm9fYXZnX3ByZXZhbGVuY2UpKQ0KcHJpbnQocGFzdGUoJ0F2ZXJhZ2UgRGV0ZWN0aW9uIFJhdGU6JywgbWFjcm9fYXZnX2RldGVjdGlvbl9yYXRlKSkNCnByaW50KHBhc3RlKCdBdmVyYWdlIEJhbGFuY2VkIEFjY3VyYWN5OicsIG1hY3JvX2F2Z19iYWxhbmNlZF9hY2N1cmFjeSkpDQoNCg0KYGBgDQoNCioqQ29tcGFyaW5nIERlY2lzaW9uIFRyZWUgUmVzdWx0cyBVc2luZyBJbmZyb21hdGlvbiBnYWluOioqDQpBZnRlciB0cmFpbmluZyB0aHJlZSB0cmVlcyB3aXRoIGRpZmZlcmVudCBzaXplcywgZW1wbG95aW5nIGluZm9ybWF0aW9uIGdhaW4gYXMgdGhlIHNlbGVjdGlvbiBtZWFzdXJlLCBvdXIgYW5hbHlzaXMgbGVkIHRvIGNvbnNpc3RlbnQgYWNjdXJhY3kgcmVzdWx0cyBhbW9uZyB0aGUgdHJlZXM6IFRyZWUgMSAoMC44OTMyKSwgVHJlZSAyICgwLjkwNTcpLCBhbmQgVHJlZSAzICgwLjg3MjEpLiBUaGUgbWlub3IgZGlzY3JlcGFuY2llcyBvYnNlcnZlZCBpbiB0aGVzZSBhY2N1cmFjeSB2YWx1ZXMgY291bGQgYmUgYXR0cmlidXRlZCB0byB0aGUgdmFyaWF0aW9ucyBpbiBkYXRhc2V0IHNpemVzLiBJbnZlc3RpZ2F0aW5nIHRoZSBpbXBhY3Qgb2YgZGlmZmVyZW50IHRyYWluaW5nIHNldCBzaXplcyBvbiBtb2RlbCBwZXJmb3JtYW5jZSBvZmZlcnMgdmFsdWFibGUgaW5zaWdodHMgaW50byB0aGUgaW50cmljYXRlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGRhdGEgc2l6ZSBhbmQgYWNjdXJhY3kuDQoNCkluIHRoZSBjYXNlIG9mIFRyZWUgMiwgd2hlcmUgYSBsYXJnZXIgdHJhaW5pbmcgc2V0IHdhcyBlbXBsb3llZCAoODAlIHRyYWluaW5nLCAyMCUgdGVzdGluZyksIHRoZSBtb2RlbCBoYWQgdGhlIG9wcG9ydHVuaXR5IHRvIGdyYXNwIG1vcmUgcm9idXN0IHBhdHRlcm5zIGFuZCByZWxhdGlvbnNoaXBzIHdpdGhpbiB0aGUgZGF0YS4gSG93ZXZlciwgaXQgaXMgY3J1Y2lhbCB0byB1bmRlcnNjb3JlIHRoZSBuZWNlc3NpdHkgb2Ygc3RyaWtpbmcgYSBiYWxhbmNlIGJldHdlZW4gdGhlIHNpemVzIG9mIHRoZSB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzLiBGb3IgVHJlZSAzLCB3aXRoIGEgcmVsYXRpdmVseSBzbWFsbGVyIHRlc3Rpbmcgc2V0ICg5MCUgdHJhaW5pbmcsIDEwJSB0ZXN0aW5nKSwgdGhlIGFjY3VyYWN5IGVzdGltYXRlIG1pZ2h0IGJlIGxlc3MgcmVsaWFibGUgZHVlIHRvIHRoZSBsaW1pdGVkIHNhbXBsZSBzaXplIGluIHRoZSB0ZXN0aW5nIHNldC4NCg0KSW4gc3VtbWFyeSwgdGhlIHV0aWxpemF0aW9uIG9mIGluZm9ybWF0aW9uIGdhaW4gYXMgdGhlIHNlbGVjdGlvbiBtZWFzdXJlLCBjb3VwbGVkIHdpdGggZGlmZmVyZW50IHRyYWluaW5nIHNldCBzaXplcywgcmVzdWx0ZWQgaW4gY29tcGFyYWJsZSBhY2N1cmFjeSBvdXRjb21lcy4gQWNoaWV2aW5nIGFuIG9wdGltYWwgYmFsYW5jZSBpbiB0aGUgc2l6ZXMgb2YgYm90aCB0cmFpbmluZyBhbmQgdGVzdGluZyBkYXRhc2V0cyBwcm92ZXMgZXNzZW50aWFsIGZvciBlbnN1cmluZyBhY2N1cmF0ZSBhbmQgZ2VuZXJhbGl6YWJsZSBtb2RlbCBwZXJmb3JtYW5jZS4NCg0KDQoNCioqR2luaSBpbmRleCoqDQoNClRoZSBHaW5pIGluZGV4LCBpcyBhIG1lYXN1cmUgdXNlZCBpbiBkZWNpc2lvbiB0cmVlcywgc3BlY2lmaWNhbGx5IGluIHRoZSBDQVJUIChDbGFzc2lmaWNhdGlvbiBhbmQgUmVncmVzc2lvbiBUcmVlcykgYWxnb3JpdGhtLCB0byBxdWFudGlmeSBob3cgb2Z0ZW4gYSByYW5kb21seSBjaG9zZW4gZWxlbWVudCB3b3VsZCBiZSBpbmNvcnJlY3RseSBsYWJlbGVkIGlmIGl0IHdhcyByYW5kb21seSBsYWJlbGVkIGFjY29yZGluZyB0byB0aGUgZGlzdHJpYnV0aW9uIG9mIGxhYmVscyBpbiB0aGUgc3Vic2V0LiBJdCByZWZsZWN0cyB0aGUgcHJvYmFiaWxpdHkgb2YgYSBwYXJ0aWN1bGFyIHZhcmlhYmxlIGJlaW5nIHdyb25nbHkgY2xhc3NpZmllZCB3aGVuIGl0IGlzIHJhbmRvbWx5IGNob3Nlbi4NCg0KKioxLUdpbmkgaW5kZXgoODAlLDIwJSkqKg0KDQpJbnN0YWxsIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCg0KYGBge3J9DQppbnN0YWxsLnBhY2thZ2VzKCJycGFydCIpDQppbnN0YWxsLnBhY2thZ2VzKCJycGFydC5wbG90IikNCmluc3RhbGwucGFja2FnZXMoImNhVG9vbHMiKQ0KaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQ0KDQpgYGANCg0KTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzDQoNCmBgYHtyfQ0KbGlicmFyeShycGFydCkNCmxpYnJhcnkocnBhcnQucGxvdCkNCmxpYnJhcnkoY2FUb29scykNCmxpYnJhcnkoY2FyZXQpDQpgYGANCg0KU2V0IGEgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5DQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKQ0KDQpgYGANCg0KU3BsaXQgdGhlIGRhdGFzZXQsIDgwJSBmb3IgdHJhaW5pbmcsIDIwJSBmb3IgdGVzdGluZw0KDQpgYGB7cn0NCnNwbGl0IDwtIHNhbXBsZS5zcGxpdChIb2JieV9EYXRhJGBQcmVkaWN0ZWQgSG9iYnlgLCBTcGxpdFJhdGlvID0gMC44MCkNCmBgYA0KDQpDcmVhdGUgdGhlIHRyYWluaW5nIHNldCAoODAlIG9mIHRoZSBkYXRhKQ0KDQpgYGB7cn0NCnRyYWluaW5nX3NldCA8LSBzdWJzZXQoSG9iYnlfRGF0YSwgc3BsaXQgPT0gVFJVRSkNCmBgYA0KDQpDcmVhdGUgdGhlIHRlc3Qgc2V0ICgyMCUgb2YgdGhlIGRhdGEpDQoNCmBgYHtyfQ0KdGVzdF9zZXQgPC0gc3Vic2V0KEhvYmJ5X0RhdGEsIHNwbGl0ID09IEZBTFNFKQ0KDQpgYGANCg0KQnVpbGQgYSBkZWNpc2lvbiB0cmVlIG1vZGVsIG9uIHRoZSB0cmFpbmluZyBzZXQNCg0KYGBge3J9DQp0cmVlIDwtIHJwYXJ0KGBQcmVkaWN0ZWQgSG9iYnlgIH4gLiwgZGF0YSA9IHRyYWluaW5nX3NldCwgbWV0aG9kID0gJ2NsYXNzJykNCg0KYGBgDQoNCg0KTWFrZSBwcmVkaWN0aW9ucyBvbiB0aGUgdGVzdCBzZXQgdXNpbmcgdGhlIHRyZWUgbW9kZWwNCg0KYGBge3J9DQpwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KHRyZWUsIHRlc3Rfc2V0LCB0eXBlID0gImNsYXNzIikNCmBgYA0KDQpDb25mdXNpb24gbWF0cml4DQoNCmBgYHtyfQ0KY29uZl9tYXRyaXggPC0gdGFibGUoUHJlZGljdGVkID0gcHJlZGljdGlvbnMsIEFjdHVhbCA9IHRlc3Rfc2V0JGBQcmVkaWN0ZWQgSG9iYnlgKQ0KYGBgDQoNCkNhbGN1bGF0ZSBhY2N1cmFjeQ0KDQpgYGB7cn0NCmFjY3VyYWN5IDwtIHN1bShkaWFnKGNvbmZfbWF0cml4KSkgLyBzdW0oY29uZl9tYXRyaXgpDQpgYGANCg0KSW5pdGlhbGl6ZSB2ZWN0b3JzIHRvIGhvbGQgdGhlIG1ldHJpY3MgZm9yIGVhY2ggY2xhc3MNCg0KYGBge3J9DQpwcmVjaXNpb24gPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCnJlY2FsbCA8LSBudW1lcmljKGxlbmd0aCA9IG5yb3coY29uZl9tYXRyaXgpKQ0Kc3BlY2lmaWNpdHkgPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCmBgYA0KDQpDYWxjdWxhdGUgbWV0cmljcyBmb3IgZWFjaCBjbGFzcw0KDQpgYGB7cn0NCmZvciAoaSBpbiAxOm5yb3coY29uZl9tYXRyaXgpKSB7DQogIFRQIDwtIGNvbmZfbWF0cml4W2ksIGldDQogIEZQIDwtIHN1bShjb25mX21hdHJpeFssIGldKSAtIFRQDQogIEZOIDwtIHN1bShjb25mX21hdHJpeFtpLCBdKSAtIFRQDQogIFROIDwtIHN1bShjb25mX21hdHJpeCkgLSBUUCAtIEZQIC0gRk4NCiAgDQogIHByZWNpc2lvbltpXSA8LSBUUCAvIChUUCArIEZQKQ0KICByZWNhbGxbaV0gPC0gVFAgLyAoVFAgKyBGTikNCiAgc3BlY2lmaWNpdHlbaV0gPC0gVE4gLyAoVE4gKyBGUCkNCn0NCmBgYA0KDQpBdmVyYWdlIHRoZSBtZXRyaWNzIGlmIHlvdSB3YW50IGEgc2luZ2xlIHBlcmZvcm1hbmNlIG1lYXN1cmUNCg0KYGBge3J9DQphdmdfcHJlY2lzaW9uIDwtIG1lYW4ocHJlY2lzaW9uKQ0KYXZnX3JlY2FsbCA8LSBtZWFuKHJlY2FsbCkNCmF2Z19zcGVjaWZpY2l0eSA8LSBtZWFuKHNwZWNpZmljaXR5KQ0KYGBgDQoNCk91dHB1dCB0aGUgZXZhbHVhdGlvbiBtZXRyaWNzDQoNCmBgYHtyfQ0KcHJpbnQocGFzdGUoIk92ZXJhbGwgQWNjdXJhY3k6IiwgYWNjdXJhY3kpKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgUHJlY2lzaW9uOiIsIGF2Z19wcmVjaXNpb24pKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgUmVjYWxsIChTZW5zaXRpdml0eSk6IiwgYXZnX3JlY2FsbCkpDQpwcmludChwYXN0ZSgiQXZlcmFnZSBTcGVjaWZpY2l0eToiLCBhdmdfc3BlY2lmaWNpdHkpKQ0KYGBgDQoNCnRoZSBtZXRyaWNzIGZvciBlYWNoIGNsYXNzOg0KDQpgYGB7cn0NCm1ldHJpY3MgPC0gZGF0YS5mcmFtZSgNCiAgQ2xhc3MgPSByb3duYW1lcyhjb25mX21hdHJpeCksDQogIFByZWNpc2lvbiA9IHByZWNpc2lvbiwNCiAgUmVjYWxsID0gcmVjYWxsLA0KICBTcGVjaWZpY2l0eSA9IHNwZWNpZmljaXR5DQopDQpgYGANCg0KUHJpbnQgbWV0cmljcw0KDQpgYGB7cn0NCnByaW50KG1ldHJpY3MpDQoNCmBgYA0KDQpQbG90IHRoZSBkZWNpc2lvbiB0cmVlDQoNCmBgYHtyfQ0KcnBhcnQucGxvdCh0cmVlKQ0KYGBgDQoNCioqRGVjaXNpb24gVHJlZSBBbmFseXNpcyBVc2luZyBHaW5pIEluZGV4KDgwLzIwKToqKg0KIFRoZSBkZWNpc2lvbiB0cmVlIGRlbGluZWF0ZXMgaG9iYmllcyBpbnRvICdBY2FkZW1pY3MnICgxKSwgJ0FydHMnICgyKSwgYW5kICdTcG9ydHMnICgzKS4gV2l0aG91dCBhIHNwb3J0cyBob2JieSAoJ0NhcmVlcl9zcHJ0JyA9IDApLCB0aGUgbW9kZWwgc3VnZ2VzdHMgYSA2MiUgY2hhbmNlIG9mICdBY2FkZW1pY3MnLiBXaXRoIG5vIGFydHMgaG9iYnkgKCdGYW50X2FydHMnID0gMCkgYW5kICdXb25fYXJ0cycgYXQgMCBvciAyLCB0aGVyZSdzIGEgNDMlIGNoYW5jZSBvZiBhbiAnQWNhZGVtaWNzJyBjYXRlZ29yaXphdGlvbi4gQ29udmVyc2VseSwgZm9yIHRob3NlIHdpdGggYW4gYXJ0cyBob2JieSAoJ0ZhbnRfYXJ0cycgPSAxKSBhbmQgZnJlcXVlbnQgYXJ0cyBhY3Rpdml0aWVzICgnVGltZV9hcnQnIOKJpSAzKSwgVGhlc2UgbW9kZWwgc2hvdyBob3cgbGlrZWx5IHRoZSBtb2RlbCBpcyB0byBwcmVkaWN0IGVhY2ggaG9iYnkgYmFzZWQgb24gdGhlIGF0dHJpYnV0ZXMnIHNpZ25pZmljYW5jZSwgYXMgbGVhcm5lZCBmcm9tIHRoZSBkYXRhIHdpdGggYSA4MCUgdHJhaW5pbmcgcG9ydGlvbg0KDQoqKjItR2luaSBpbmRleCg5MCUsMTAlKSoqDQoNCkluc3RhbGwgbmVjZXNzYXJ5IGxpYnJhcmllcw0KDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoInJwYXJ0IikNCmluc3RhbGwucGFja2FnZXMoInJwYXJ0LnBsb3QiKQ0KaW5zdGFsbC5wYWNrYWdlcygiY2FUb29scyIpDQppbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQoNCmBgYA0KDQpMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCg0KYGBge3J9DQpsaWJyYXJ5KHJwYXJ0KQ0KbGlicmFyeShycGFydC5wbG90KQ0KbGlicmFyeShjYVRvb2xzKQ0KbGlicmFyeShjYXJldCkNCmBgYA0KDQpTZXQgYSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCg0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQoNCmBgYA0KDQpTcGxpdCB0aGUgZGF0YXNldCwgOTAlIGZvciB0cmFpbmluZywgMTAlIGZvciB0ZXN0aW5nDQoNCmBgYHtyfQ0Kc3BsaXQgPC0gc2FtcGxlLnNwbGl0KEhvYmJ5X0RhdGEkYFByZWRpY3RlZCBIb2JieWAsIFNwbGl0UmF0aW8gPSAwLjkwKQ0KYGBgDQoNCkNyZWF0ZSB0aGUgdHJhaW5pbmcgc2V0ICg5MCUgb2YgdGhlIGRhdGEpDQoNCmBgYHtyfQ0KdHJhaW5pbmdfc2V0IDwtIHN1YnNldChIb2JieV9EYXRhLCBzcGxpdCA9PSBUUlVFKQ0KYGBgDQoNCkNyZWF0ZSB0aGUgdGVzdCBzZXQgKDEwJSBvZiB0aGUgZGF0YSkNCg0KYGBge3J9DQp0ZXN0X3NldCA8LSBzdWJzZXQoSG9iYnlfRGF0YSwgc3BsaXQgPT0gRkFMU0UpDQoNCmBgYA0KDQpCdWlsZCBhIGRlY2lzaW9uIHRyZWUgbW9kZWwgb24gdGhlIHRyYWluaW5nIHNldA0KDQpgYGB7cn0NCnRyZWUgPC0gcnBhcnQoYFByZWRpY3RlZCBIb2JieWAgfiAuLCBkYXRhID0gdHJhaW5pbmdfc2V0LCBtZXRob2QgPSAnY2xhc3MnKQ0KDQpgYGANCg0KDQpNYWtlIHByZWRpY3Rpb25zIG9uIHRoZSB0ZXN0IHNldCB1c2luZyB0aGUgdHJlZSBtb2RlbA0KDQpgYGB7cn0NCnByZWRpY3Rpb25zIDwtIHByZWRpY3QodHJlZSwgdGVzdF9zZXQsIHR5cGUgPSAiY2xhc3MiKQ0KYGBgDQoNCkNvbmZ1c2lvbiBtYXRyaXgNCg0KYGBge3J9DQpjb25mX21hdHJpeCA8LSB0YWJsZShQcmVkaWN0ZWQgPSBwcmVkaWN0aW9ucywgQWN0dWFsID0gdGVzdF9zZXQkYFByZWRpY3RlZCBIb2JieWApDQpgYGANCg0KQ2FsY3VsYXRlIGFjY3VyYWN5DQoNCmBgYHtyfQ0KYWNjdXJhY3kgPC0gc3VtKGRpYWcoY29uZl9tYXRyaXgpKSAvIHN1bShjb25mX21hdHJpeCkNCmBgYA0KDQpJbml0aWFsaXplIHZlY3RvcnMgdG8gaG9sZCB0aGUgbWV0cmljcyBmb3IgZWFjaCBjbGFzcw0KDQpgYGB7cn0NCnByZWNpc2lvbiA8LSBudW1lcmljKGxlbmd0aCA9IG5yb3coY29uZl9tYXRyaXgpKQ0KcmVjYWxsIDwtIG51bWVyaWMobGVuZ3RoID0gbnJvdyhjb25mX21hdHJpeCkpDQpzcGVjaWZpY2l0eSA8LSBudW1lcmljKGxlbmd0aCA9IG5yb3coY29uZl9tYXRyaXgpKQ0KYGBgDQoNCkNhbGN1bGF0ZSBtZXRyaWNzIGZvciBlYWNoIGNsYXNzDQoNCmBgYHtyfQ0KZm9yIChpIGluIDE6bnJvdyhjb25mX21hdHJpeCkpIHsNCiAgVFAgPC0gY29uZl9tYXRyaXhbaSwgaV0NCiAgRlAgPC0gc3VtKGNvbmZfbWF0cml4WywgaV0pIC0gVFANCiAgRk4gPC0gc3VtKGNvbmZfbWF0cml4W2ksIF0pIC0gVFANCiAgVE4gPC0gc3VtKGNvbmZfbWF0cml4KSAtIFRQIC0gRlAgLSBGTg0KICANCiAgcHJlY2lzaW9uW2ldIDwtIFRQIC8gKFRQICsgRlApDQogIHJlY2FsbFtpXSA8LSBUUCAvIChUUCArIEZOKQ0KICBzcGVjaWZpY2l0eVtpXSA8LSBUTiAvIChUTiArIEZQKQ0KfQ0KYGBgDQoNCkF2ZXJhZ2UgdGhlIG1ldHJpY3MgaWYgeW91IHdhbnQgYSBzaW5nbGUgcGVyZm9ybWFuY2UgbWVhc3VyZQ0KDQpgYGB7cn0NCmF2Z19wcmVjaXNpb24gPC0gbWVhbihwcmVjaXNpb24pDQphdmdfcmVjYWxsIDwtIG1lYW4ocmVjYWxsKQ0KYXZnX3NwZWNpZmljaXR5IDwtIG1lYW4oc3BlY2lmaWNpdHkpDQpgYGANCg0KT3V0cHV0IHRoZSBldmFsdWF0aW9uIG1ldHJpY3MNCg0KYGBge3J9DQpwcmludChwYXN0ZSgiT3ZlcmFsbCBBY2N1cmFjeToiLCBhY2N1cmFjeSkpDQpwcmludChwYXN0ZSgiQXZlcmFnZSBQcmVjaXNpb246IiwgYXZnX3ByZWNpc2lvbikpDQpwcmludChwYXN0ZSgiQXZlcmFnZSBSZWNhbGwgKFNlbnNpdGl2aXR5KToiLCBhdmdfcmVjYWxsKSkNCnByaW50KHBhc3RlKCJBdmVyYWdlIFNwZWNpZmljaXR5OiIsIGF2Z19zcGVjaWZpY2l0eSkpDQpgYGANCg0KdGhlIG1ldHJpY3MgZm9yIGVhY2ggY2xhc3M6DQoNCmBgYHtyfQ0KbWV0cmljcyA8LSBkYXRhLmZyYW1lKA0KICBDbGFzcyA9IHJvd25hbWVzKGNvbmZfbWF0cml4KSwNCiAgUHJlY2lzaW9uID0gcHJlY2lzaW9uLA0KICBSZWNhbGwgPSByZWNhbGwsDQogIFNwZWNpZmljaXR5ID0gc3BlY2lmaWNpdHkNCikNCmBgYA0KDQpQcmludCBtZXRyaWNzDQoNCmBgYHtyfQ0KcHJpbnQobWV0cmljcykNCg0KYGBgDQoNCg0KUGxvdCB0aGUgZGVjaXNpb24gdHJlZQ0KDQpgYGB7cn0NCnJwYXJ0LnBsb3QodHJlZSkNCmBgYA0KDQoqKkRlY2lzaW9uIFRyZWUgQW5hbHlzaXMgVXNpbmcgR2luaSBJbmRleCg5MC8xMCk6KioNCg0KVGhlIGRlY2lzaW9uIHRyZWUgY2xhc3NpZmllcyBob2JiaWVzIGludG8gJ0FjYWRlbWljcycgKDEpLCAnQXJ0cycgKDIpLCBhbmQgJ1Nwb3J0cycgKDMpLiBBIGxhY2sgb2YgYSBzcG9ydHMgaG9iYnkgKCdDYXJlZXJfc3BydCcgPSAwKSBsZWFkcyB0byBhIDYzJSBjaGFuY2Ugb2YgZmFsbGluZyBpbnRvICdBY2FkZW1pY3MnLiBJZiBzb21lb25lIGlzIG5vdCBlbmdhZ2VkIGluIGFuIGFydHMgaG9iYnkgKCdGYW50X2FydHMnID0gMCkgYW5kICdXb25fYXJ0cycgaXMgMCBvciAyLCB0aGVyZSdzIGEgNDMlIHByb2JhYmlsaXR5IG9mIGFuICdBY2FkZW1pY3MnIGNhdGVnb3JpemF0aW9uLiBGb3IgaW5kaXZpZHVhbHMgZW5nYWdlZCBpbiBhbiBhcnRzIGhvYmJ5ICgnRmFudF9hcnRzJyA9IDEpIHdpdGggYSBoaWdoIGxldmVsIG9mIGFydHMgYWN0aXZpdHkgKCdUaW1lX2FydCcg4omlIDMpLCB0aGUgbGlrZWxpaG9vZCBvZiBhICdTcG9ydHMnIGNsYXNzaWZpY2F0aW9uIGlzIDI4JS4gVGhlc2UgbW9kZWwgc2hvdyBob3cgbGlrZWx5IHRoZSBtb2RlbCBpcyB0byBwcmVkaWN0IGVhY2ggaG9iYnkgYmFzZWQgb24gdGhlIGF0dHJpYnV0ZXMnIHNpZ25pZmljYW5jZSwgYXMgbGVhcm5lZCBmcm9tIHRoZSBkYXRhIHdpdGggYSA5MCUgdHJhaW5pbmcgcG9ydGlvbi4NCg0KDQoqKjMtR2luaSBpbmRleCg3MCUsMzAlKSoqDQoNCkluc3RhbGwgbmVjZXNzYXJ5IGxpYnJhcmllcw0KDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoInJwYXJ0IikNCmluc3RhbGwucGFja2FnZXMoInJwYXJ0LnBsb3QiKQ0KaW5zdGFsbC5wYWNrYWdlcygiY2FUb29scyIpDQppbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQoNCmBgYA0KDQpMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCg0KYGBge3J9DQpsaWJyYXJ5KHJwYXJ0KQ0KbGlicmFyeShycGFydC5wbG90KQ0KbGlicmFyeShjYVRvb2xzKQ0KbGlicmFyeShjYXJldCkNCmBgYA0KDQpTZXQgYSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCg0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQoNCmBgYA0KDQpTcGxpdCB0aGUgZGF0YXNldCwgNzAlIGZvciB0cmFpbmluZywgMzAlIGZvciB0ZXN0aW5nDQoNCmBgYHtyfQ0Kc3BsaXQgPC0gc2FtcGxlLnNwbGl0KEhvYmJ5X0RhdGEkYFByZWRpY3RlZCBIb2JieWAsIFNwbGl0UmF0aW8gPSAwLjcwKQ0KYGBgDQoNCkNyZWF0ZSB0aGUgdHJhaW5pbmcgc2V0ICg3MCUgb2YgdGhlIGRhdGEpDQoNCmBgYHtyfQ0KdHJhaW5pbmdfc2V0IDwtIHN1YnNldChIb2JieV9EYXRhLCBzcGxpdCA9PSBUUlVFKQ0KYGBgDQoNCkNyZWF0ZSB0aGUgdGVzdCBzZXQgKDIwJSBvZiB0aGUgZGF0YSkNCg0KYGBge3J9DQp0ZXN0X3NldCA8LSBzdWJzZXQoSG9iYnlfRGF0YSwgc3BsaXQgPT0gRkFMU0UpDQoNCmBgYA0KDQpCdWlsZCBhIGRlY2lzaW9uIHRyZWUgbW9kZWwgb24gdGhlIHRyYWluaW5nIHNldA0KDQpgYGB7cn0NCnRyZWUgPC0gcnBhcnQoYFByZWRpY3RlZCBIb2JieWAgfiAuLCBkYXRhID0gdHJhaW5pbmdfc2V0LCBtZXRob2QgPSAnY2xhc3MnKQ0KDQpgYGANCg0KTWFrZSBwcmVkaWN0aW9ucyBvbiB0aGUgdGVzdCBzZXQgdXNpbmcgdGhlIHRyZWUgbW9kZWwNCg0KYGBge3J9DQpwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KHRyZWUsIHRlc3Rfc2V0LCB0eXBlID0gImNsYXNzIikNCmBgYA0KDQpDb25mdXNpb24gbWF0cml4DQoNCmBgYHtyfQ0KY29uZl9tYXRyaXggPC0gdGFibGUoUHJlZGljdGVkID0gcHJlZGljdGlvbnMsIEFjdHVhbCA9IHRlc3Rfc2V0JGBQcmVkaWN0ZWQgSG9iYnlgKQ0KYGBgDQoNCkNhbGN1bGF0ZSBhY2N1cmFjeQ0KDQpgYGB7cn0NCmFjY3VyYWN5IDwtIHN1bShkaWFnKGNvbmZfbWF0cml4KSkgLyBzdW0oY29uZl9tYXRyaXgpDQpgYGANCg0KSW5pdGlhbGl6ZSB2ZWN0b3JzIHRvIGhvbGQgdGhlIG1ldHJpY3MgZm9yIGVhY2ggY2xhc3MNCg0KYGBge3J9DQpwcmVjaXNpb24gPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCnJlY2FsbCA8LSBudW1lcmljKGxlbmd0aCA9IG5yb3coY29uZl9tYXRyaXgpKQ0Kc3BlY2lmaWNpdHkgPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCmBgYA0KDQpDYWxjdWxhdGUgbWV0cmljcyBmb3IgZWFjaCBjbGFzcw0KDQpgYGB7cn0NCmZvciAoaSBpbiAxOm5yb3coY29uZl9tYXRyaXgpKSB7DQogIFRQIDwtIGNvbmZfbWF0cml4W2ksIGldDQogIEZQIDwtIHN1bShjb25mX21hdHJpeFssIGldKSAtIFRQDQogIEZOIDwtIHN1bShjb25mX21hdHJpeFtpLCBdKSAtIFRQDQogIFROIDwtIHN1bShjb25mX21hdHJpeCkgLSBUUCAtIEZQIC0gRk4NCiAgDQogIHByZWNpc2lvbltpXSA8LSBUUCAvIChUUCArIEZQKQ0KICByZWNhbGxbaV0gPC0gVFAgLyAoVFAgKyBGTikNCiAgc3BlY2lmaWNpdHlbaV0gPC0gVE4gLyAoVE4gKyBGUCkNCn0NCmBgYA0KDQpBdmVyYWdlIHRoZSBtZXRyaWNzIGlmIHlvdSB3YW50IGEgc2luZ2xlIHBlcmZvcm1hbmNlIG1lYXN1cmUNCg0KYGBge3J9DQphdmdfcHJlY2lzaW9uIDwtIG1lYW4ocHJlY2lzaW9uKQ0KYXZnX3JlY2FsbCA8LSBtZWFuKHJlY2FsbCkNCmF2Z19zcGVjaWZpY2l0eSA8LSBtZWFuKHNwZWNpZmljaXR5KQ0KYGBgDQoNCk91dHB1dCB0aGUgZXZhbHVhdGlvbiBtZXRyaWNzDQoNCmBgYHtyfQ0KcHJpbnQocGFzdGUoIk92ZXJhbGwgQWNjdXJhY3k6IiwgYWNjdXJhY3kpKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgUHJlY2lzaW9uOiIsIGF2Z19wcmVjaXNpb24pKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgUmVjYWxsIChTZW5zaXRpdml0eSk6IiwgYXZnX3JlY2FsbCkpDQpwcmludChwYXN0ZSgiQXZlcmFnZSBTcGVjaWZpY2l0eToiLCBhdmdfc3BlY2lmaWNpdHkpKQ0KYGBgDQoNCnRoZSBtZXRyaWNzIGZvciBlYWNoIGNsYXNzOg0KDQpgYGB7cn0NCm1ldHJpY3MgPC0gZGF0YS5mcmFtZSgNCiAgQ2xhc3MgPSByb3duYW1lcyhjb25mX21hdHJpeCksDQogIFByZWNpc2lvbiA9IHByZWNpc2lvbiwNCiAgUmVjYWxsID0gcmVjYWxsLA0KICBTcGVjaWZpY2l0eSA9IHNwZWNpZmljaXR5DQopDQpgYGANCg0KUHJpbnQgbWV0cmljcw0KDQpgYGB7cn0NCnByaW50KG1ldHJpY3MpDQoNCmBgYA0KDQpQbG90IHRoZSBkZWNpc2lvbiB0cmVlDQoNCmBgYHtyfQ0KcnBhcnQucGxvdCh0cmVlKQ0KYGBgDQoNCioqRGVjaXNpb24gVHJlZSBBbmFseXNpcyBVc2luZyBHaW5pIEluZGV4KDcwLzMwKToqKg0KDQpUaGUgZGVjaXNpb24gdHJlZSBzb3J0cyBob2JiaWVzIGludG8gJ0FjYWRlbWljcycgKDEpLCAnQXJ0cycgKDIpLCBhbmQgJ1Nwb3J0cycgKDMpLiBBIG5vbi1zcG9ydHMgaG9iYnkgKCdDYXJlZXJfc3BydCcgPSAwKSByZXN1bHRzIGluIGEgNjMlIHByb2JhYmlsaXR5IG9mIGFuICdBY2FkZW1pY3MnIGNhdGVnb3JpemF0aW9uLiBJZiAnRmFudF9hcnRzJyBpcyAwIGFuZCAnV29uX2FydHMnIGlzIDAgb3IgMiwgdGhlcmUncyBhIDQzJSBjaGFuY2Ugb2YgYmVpbmcgY2xhc3NpZmllZCBhcyAnQWNhZGVtaWNzJy4gQ29udmVyc2VseSwgZm9yIHRob3NlIGludm9sdmVkIGluIGFuIGFydHMgaG9iYnkgKCdGYW50X2FydHMnID0gMSkgd2l0aCBzaWduaWZpY2FudCBhcnRzIGFjdGl2aXR5ICgnVGltZV9hcnQnIOKJpSAzKSwgdGhlIG1vZGVsIGluZGljYXRlcyBhIDI4JSBwcm9iYWJpbGl0eSBvZiBhICdTcG9ydHMnIGhvYmJ5LiBUaGlzIGRlY2lzaW9uIHRyZWUgZGVtb25zdHJhdGVzIHRoZSBsaWtlbGlob29kIG9mIHByZWRpY3RpbmcgZWFjaCBob2JieSBiYXNlZCBvbiB0aGUgaW1wb3J0YW5jZSBvZiB0aGUgYXR0cmlidXRlcywgYXMgZGV0ZXJtaW5lZCBmcm9tIHRoZSBkYXRhIHRyYWluZWQgd2l0aCBhIDcwJSBwb3J0aW9uLg0KDQoqKkNvbXBhcmluZyBEZWNpc2lvbiBUcmVlIFJlc3VsdHMgVXNpbmcgR2luaSBJbmRleDoqKg0KDQpBY3Jvc3MgVGhyZWUgVHJhaW5pbmctVGVzdCBTaXplczogVGhlIHJlc3VsdHMgb2YgdGhlIGRlY2lzaW9uIHRyZWVzIGZyb20gdGhlIDkwLzEwLCA4MC8yMCwgYW5kIDcwLzMwIGRhdGFzZXQgc3BsaXRzLCB0aGVyZSBpcyBhIGNvbnNpc3RlbnQgcGF0dGVybjogJ0NhcmVlcl9zcHJ0JyBpcyBhbHdheXMgdGhlIHJvb3Qgbm9kZSwgYW5kIHRoZSBzdWJzZXF1ZW50IHNwbGl0cyBvbiAnV29uX2FydHMnIGFuZCAnRmFudF9hcnRzJyBhcmUgdGhlIHNhbWUgYWNyb3NzIGFsbCB0cmVlcy4gVGhpcyBjb25zaXN0ZW5jeSBpbiB0cmVlIHN0cnVjdHVyZSBhbmQgdGhlIHByb2JhYmlsaXRpZXMgZm9yIHByZWRpY3RpbmcgJ0FjYWRlbWljcycgYW5kICdTcG9ydHMnIGFjcm9zcyBkaWZmZXJlbnQgc3BsaXRzIHN1Z2dlc3QgYSBzdGFibGUgYW5kIHJvYnVzdCBtb2RlbCB0aGF0IGlzIHJlbGlhYmxlIHJlZ2FyZGxlc3Mgb2YgdGhlIHRyYWluaW5nIHNldCBzaXplLg0KDQp0aGUgYWNjdXJhY2llcyBvZiB0aHJlZSBkYXRhIHNwbGl0cyByZXZlYWxzIGRpc3RpbmN0IG91dGNvbWVzOiB0aGUgKDkwLDEwKSBzcGxpdCBsZWFkcyB3aXRoIHRoZSBoaWdoZXN0IGFjY3VyYWN5IGF0IDAuOTE4NzUsIHN1Z2dlc3RpbmcgdGhhdCBhIGxhcmdlciB0cmFpbmluZyBwb3J0aW9uIGlzIG1vcmUgZWZmZWN0aXZlIGluIHRoaXMgY2FzZS4gVGhlICg3MCwzMCkgc3BsaXQgZm9sbG93cyB3aXRoIGFuIGFjY3VyYWN5IG9mIDAuOTEwNjAsIHNob3dpbmcgc3Ryb25nIHBlcmZvcm1hbmNlIGV2ZW4gd2l0aCBhIGxhcmdlciB0ZXN0IHNldC4gSG93ZXZlciwgdGhlIGNvbW1vbmx5IHVzZWQgKDgwLDIwKSBzcGxpdCBsYWdzIHNsaWdodGx5IGJlaGluZCwgYWNoaWV2aW5nIGFuIGFjY3VyYWN5IG9mIDAuOTA2MjUuIFRoaXMgY29tcGFyaXNvbiBoaWdobGlnaHRzIHRoZSBpbXBhY3Qgb2YgdmFyeWluZyB0cmFpbmluZyBhbmQgdGVzdGluZyBwcm9wb3J0aW9ucyBvbiBtb2RlbCBhY2N1cmFjeS4NCg0KDQoNCioqR2FpbiBSYXRpbyoqDQpUaGUgdGhpcmQgY3JpdGVyaW9uIGVtcGxveWVkIGZvciBidWlsZGluZyB0aGUgZGVjaXNpb24gdHJlZSBpcyBHYWluIFJhdGlvLiBHYWluIFJhdGlvIHN0YW5kcyBvdXQgYXMgYSBzaWduaWZpY2FudCBtZXRyaWMgaW4gZGVjaXNpb24gdHJlZSBhbGdvcml0aG1zLCBlc3BlY2lhbGx5IGluIHNjZW5hcmlvcyBpbnZvbHZpbmcgY2F0ZWdvcmljYWwgdGFyZ2V0IHZhcmlhYmxlcy4gSXQgbm9ybWFsaXplcyB0aGUgcmVkdWN0aW9uIGluIGVudHJvcHkgYnkgdGFraW5nIGludG8gYWNjb3VudCB0aGUgcG90ZW50aWFsIGluZm9ybWF0aW9uIGNvbnRlbnQgb2YgdGhlIGZlYXR1cmUuIFRoaXMgbm9ybWFsaXphdGlvbiBwcm9jZXNzIG1ha2VzIEdhaW4gUmF0aW8gcGFydGljdWxhcmx5IHN1aXRhYmxlIGZvciBkYXRhc2V0cyB3aXRoIGNhdGVnb3JpY2FsIHRhcmdldCB2YXJpYWJsZXMuIEJ5IGZhY3RvcmluZyBpbiB0aGUgaW50cmluc2ljIGluZm9ybWF0aW9uIG9mIGEgc3BsaXQsIEdhaW4gUmF0aW8gZWZmZWN0aXZlbHkgbWl0aWdhdGVzIGJpYXMgdG93YXJkcyBmZWF0dXJlcyB3aXRoIGhpZ2hlciBsZXZlbHMsIGVuc3VyaW5nIGEgbW9yZSBiYWxhbmNlZCBldmFsdWF0aW9uIG9mIGRpZmZlcmVudCBhdHRyaWJ1dGVzLg0KDQoqKjEtR2FpbiByYXRpbyg5MCUsMTAlKSoqDQoNCkluc3RhbGwgbmVjZXNzYXJ5IGxpYnJhcmllcw0KDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoIkM1MCIpDQppbnN0YWxsLnBhY2thZ2VzKCJwcmludHIiKQ0KaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQ0KYGBgDQoNCkxvYWQgbmVjZXNzYXJ5IGxpYnJhcmllcw0KDQpgYGB7cn0NCg0KbGlicmFyeShDNTApDQpsaWJyYXJ5KHByaW50cikNCmxpYnJhcnkoY2FyZXQpDQpgYGANCg0KU2V0IGEgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5DQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTk1OCkNCg0KYGBgDQoNClNwbGl0dGluZyB0aGUgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0IHNldHMNCg0KYGBge3J9DQp0cmFpbl9pbmRpY2VzIDwtIHNhbXBsZSgxOm5yb3coSG9iYnlfRGF0YSksIDAuOSAqIG5yb3coSG9iYnlfRGF0YSkpDQpIb2JieS50cmFpbiA8LSBIb2JieV9EYXRhW3RyYWluX2luZGljZXMsIF0NCkhvYmJ5LnRlc3QgPC0gSG9iYnlfRGF0YVstdHJhaW5faW5kaWNlcywgXQ0KDQpgYGANCg0KVHJhaW5pbmcgdGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwNCg0KYGBge3J9DQptb2RlbCA8LSBDNS4wKGBQcmVkaWN0ZWQgSG9iYnlgIH4gLiwgZGF0YSA9IEhvYmJ5LnRyYWluLCBjb250cm9sID0gQzUuMENvbnRyb2woQ0YgPSAwLjAxKSkNCg0KYGBgDQoNCk1ha2luZyBwcmVkaWN0aW9ucyBvbiB0aGUgdGVzdCBzZXQNCg0KYGBge3J9DQpwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KG1vZGVsLCBuZXdkYXRhID0gSG9iYnkudGVzdCwgdHlwZSA9ICdjbGFzcycpDQpgYGANCg0KQ3JlYXRlIGEgY29uZnVzaW9uIG1hdHJpeCBmcm9tIHRoZSBwcmVkaWN0aW9ucyBhbmQgYWN0dWFsIHZhbHVlcw0KDQpgYGB7cn0NCmNvbmZfbWF0cml4IDwtIHRhYmxlKFByZWRpY3RlZCA9IHByZWRpY3Rpb25zLCBBY3R1YWwgPSBIb2JieS50ZXN0JGBQcmVkaWN0ZWQgSG9iYnlgKQ0KYGBgDQoNCkNhbGN1bGF0ZSBhbmQgcHJpbnQgdGhlIGFjY3VyYWN5IG9mIHRoZSBtb2RlbA0KDQpgYGB7cn0NCmFjY3VyYWN5IDwtIHN1bShkaWFnKGNvbmZfbWF0cml4KSkgLyBzdW0oY29uZl9tYXRyaXgpDQpwcmludChwYXN0ZSgnQWNjdXJhY3kgb24gdGVzdCBkYXRhIGlzOicsIGFjY3VyYWN5KSkNCmBgYA0KDQpJbml0aWFsaXplIHZlY3RvcnMgdG8gaG9sZCB0aGUgbWV0cmljcyBmb3IgZWFjaCBjbGFzcw0KDQpgYGB7cn0NCnByZWNpc2lvbiA8LSBudW1lcmljKGxlbmd0aCA9IG5yb3coY29uZl9tYXRyaXgpKQ0KcmVjYWxsIDwtIG51bWVyaWMobGVuZ3RoID0gbnJvdyhjb25mX21hdHJpeCkpDQpzcGVjaWZpY2l0eSA8LSBudW1lcmljKGxlbmd0aCA9IG5yb3coY29uZl9tYXRyaXgpKQ0KYGBgDQoNCkNhbGN1bGF0ZSBtZXRyaWNzIGZvciBlYWNoIGNsYXNzDQoNCmBgYHtyfQ0KZm9yIChpIGluIDE6bnJvdyhjb25mX21hdHJpeCkpIHsNCiAgVFAgPC0gY29uZl9tYXRyaXhbaSwgaV0NCiAgRlAgPC0gc3VtKGNvbmZfbWF0cml4WywgaV0pIC0gVFANCiAgRk4gPC0gc3VtKGNvbmZfbWF0cml4W2ksIF0pIC0gVFANCiAgVE4gPC0gc3VtKGNvbmZfbWF0cml4KSAtIFRQIC0gRlAgLSBGTg0KICANCiAgcHJlY2lzaW9uW2ldIDwtIFRQIC8gKFRQICsgRlApDQogIHJlY2FsbFtpXSA8LSBUUCAvIChUUCArIEZOKQ0KICBzcGVjaWZpY2l0eVtpXSA8LSBUTiAvIChUTiArIEZQKQ0KfQ0KYGBgDQoNCkF2ZXJhZ2UgdGhlIG1ldHJpY3MgaWYgeW91IHdhbnQgYSBzaW5nbGUgcGVyZm9ybWFuY2UgbWVhc3VyZQ0KDQpgYGB7cn0NCmF2Z19wcmVjaXNpb24gPC0gbWVhbihwcmVjaXNpb24pDQphdmdfcmVjYWxsIDwtIG1lYW4ocmVjYWxsKQ0KYXZnX3NwZWNpZmljaXR5IDwtIG1lYW4oc3BlY2lmaWNpdHkpDQpgYGANCg0KT3V0cHV0IHRoZSBldmFsdWF0aW9uIG1ldHJpY3MNCg0KYGBge3J9DQpwcmludChwYXN0ZSgiT3ZlcmFsbCBBY2N1cmFjeToiLCBhY2N1cmFjeSkpDQpwcmludChwYXN0ZSgiQXZlcmFnZSBQcmVjaXNpb246IiwgYXZnX3ByZWNpc2lvbikpDQpwcmludChwYXN0ZSgiQXZlcmFnZSBSZWNhbGwgKFNlbnNpdGl2aXR5KToiLCBhdmdfcmVjYWxsKSkNCnByaW50KHBhc3RlKCJBdmVyYWdlIFNwZWNpZmljaXR5OiIsIGF2Z19zcGVjaWZpY2l0eSkpDQpgYGANCg0KcHJpbnQgdGhlIG1ldHJpY3MgZm9yIGVhY2ggY2xhc3M6DQoNCmBgYHtyfQ0KDQptZXRyaWNzIDwtIGRhdGEuZnJhbWUoDQogIENsYXNzID0gcm93bmFtZXMoY29uZl9tYXRyaXgpLA0KICBQcmVjaXNpb24gPSBwcmVjaXNpb24sDQogIFJlY2FsbCA9IHJlY2FsbCwNCiAgU3BlY2lmaWNpdHkgPSBzcGVjaWZpY2l0eQ0KKQ0KYGBgDQoNClByaW50IG1ldHJpY3MNCg0KYGBge3J9DQpwcmludChtZXRyaWNzKQ0KYGBgDQoNCkdlbmVyYXRlIGFuZCBwcmludCBhZGRpdGlvbmFsIHBlcmZvcm1hbmNlIG1ldHJpY3MgdXNpbmcgY2FyZXQgcGFja2FnZQ0KDQpgYGB7cn0NCmNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9ucywgSG9iYnkudGVzdCRgUHJlZGljdGVkIEhvYmJ5YCkNCmBgYA0KDQpQbG90IHRoZSBkZWNpc2lvbiB0cmVlDQoNCmBgYHtyfQ0KcGxvdChtb2RlbCkNCmBgYA0KDQoqKkRlY2lzaW9uIFRyZWUgQW5hbHlzaXMgVXNpbmcgR2FpbiBSYXRpbyg5MCUvMTAlKToqKg0KDQpJbiBGaXJzdCBUcmVlICx3ZSBkZXZpZGUgZGF0YXNldCBpbnRvIHRyYWluaW5nIHNldCBhbmQgdGVzdCBzZXQgd2l0aCBzaXplKCU5MCwlMTApIHJlc3BlY3RpdmVseS4gQXMgeW91IGNhbiBzZWUgaW4gdGhlIGZpZ3VyZSwgdGhlIHJvb3Qgbm9kZSBpcyAiQ2FyZWVyX3NwcnQiICwgY2xhc3MgMSgiQWNhZGVtaWNzIiksIGNsYXNzIDIoIkFydHMiKSwgYW5kIGNsYXNzIDMgKCJTcG9ydHMiKS4NCg0KTm9kZSAiQ2FyZWVyX3NwcnQiIFRoZSBmaXJzdCBkZWNpc2lvbiBpcyBiYXNlZCBvbiB3aGV0aGVyIHRoZSB2YWx1ZSBvZiB0aGUgIkNhcmVlcl9zcHJ0IiBhdHRyaWJ1dGUgaXMgMC4gdGhlbiBjaGVjayBpZiAiV29uX2FydHMiIGlzIGVpdGhlciAwIG9yIDIuSWYiIFdvbl9hcnRzIiBpcyAwIG9yIDIgLCBwcmVkaWN0IkFjYWRlbWljIi50aGVuIGNoZWNrIElmICJGYW50X2FydHMiIGlzIDEgImFuZCBXb25fYXJ0cyIgaXMgMSwgcHJlZGljdCAiQXJ0cyIuSWYgIkZhbnRfYXJ0cyIgaXMgMCBhbmQgV29uX2FydHMgaXMgMCBvciAyLCB0aGVuIGNoZWNrIGlmIE9seW1waWFkX1BhcnRpY2lwYXRpb24gaXMgMS5JZiBPbHltcGlhZF9QYXJ0aWNpcGF0aW9uIGlzIDEgcHJlZGljdCAiQWNhZGVtaWNzIi4oV2hlbiBPbHltcGlhZF9QYXJ0aWNpcGF0aW9uIGlzIDApIElmICJPbHltcGlhZF9QYXJ0aWNpcGF0aW9uIiBpcyAwIGFuZCAiRmFudF9hcnRzIiBpcyAwLCB0aGVuIGNoZWNrIGlmICJHcmFzcF9wb3ciIGlzIGxlc3MgdGhhbiBvciBlcXVhbCB0byA0ICwgcHJlZGljdCBjbGFzcyJBcnRzIi5XaGVuIENhcmVlcl9zcHJ0IGlzIDEsIHRoZW4gY2hlY2sgaWYgIkZhbnRfYXJ0cyIgaXMgMCB0aGVuIHByZWRpY3QgIlNwb3J0cyIuIElmICJGYW50X2FydHMiIGlzIDEsIHRoZW4gY2hlY2sgaWYgIlRpbWVfYXJ0IiBpcyBncmVhdGVyIHRoYW4gMi5jaGVjayBpZiAiVGltZV9hcnQiIGlzIGxlc3MgdGhhbiBvciBlcXVhbCB0byAyLCB0aGVuIGNoZWNrIGlmICJBY3Rfc3BydCIgaXMgMSBvciAwLklmIEFjdF9zcHJ0IGlzIDEsIHByZWRpY3QgIlNwb3J0cyIuIElmIEFjdF9zcHJ0IGlzIDAsIHRoZW4gY2hlY2sgaWYgT2x5bXBpYWRfUGFydGljaXBhdGlvbiBpcyAwIHByZWRpY3QgIkFydHMiLiBJZiAiT2x5bXBpYWRfUGFydGljaXBhdGlvbiIgaXMgMSwgcHJlZGljdCAiQWNhZGVtaWNzIi5XaGVuIFRpbWVfYXJ0IGlzIGdyZWF0ZXIgdGhhbiAyICxJZiBXb25fYXJ0cyBpcyAwLCBwcmVkaWN0IGNsYXNzICJTcG9ydHMiLg0KDQoNCioqMi1HYWluIHJhdGlvKDgwJSwyMCUpKioNCg0KSW5zdGFsbCBuZWNlc3NhcnkgbGlicmFyaWVzDQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygiQzUwIikNCmluc3RhbGwucGFja2FnZXMoInByaW50ciIpDQppbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQpgYGANCg0KTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzDQoNCmBgYHtyfQ0KDQpsaWJyYXJ5KEM1MCkNCmxpYnJhcnkocHJpbnRyKQ0KbGlicmFyeShjYXJldCkNCmBgYA0KDQpTZXQgYSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCg0KYGBge3J9DQpzZXQuc2VlZCgxOTU4KQ0KDQpgYGANCg0KU3BsaXR0aW5nIHRoZSBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3Qgc2V0cw0KDQpgYGB7cn0NCnRyYWluX2luZGljZXMgPC0gc2FtcGxlKDE6bnJvdyhIb2JieV9EYXRhKSwgMC44ICogbnJvdyhIb2JieV9EYXRhKSkNCkhvYmJ5LnRyYWluIDwtIEhvYmJ5X0RhdGFbdHJhaW5faW5kaWNlcywgXQ0KSG9iYnkudGVzdCA8LSBIb2JieV9EYXRhWy10cmFpbl9pbmRpY2VzLCBdDQoNCmBgYA0KDQpUcmFpbmluZyB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbA0KDQpgYGB7cn0NCm1vZGVsIDwtIEM1LjAoYFByZWRpY3RlZCBIb2JieWAgfiAuLCBkYXRhID0gSG9iYnkudHJhaW4sIGNvbnRyb2wgPSBDNS4wQ29udHJvbChDRiA9IDAuMDEpKQ0KDQpgYGANCg0KTWFraW5nIHByZWRpY3Rpb25zIG9uIHRoZSB0ZXN0IHNldA0KDQpgYGB7cn0NCnByZWRpY3Rpb25zIDwtIHByZWRpY3QobW9kZWwsIG5ld2RhdGEgPSBIb2JieS50ZXN0LCB0eXBlID0gJ2NsYXNzJykNCmBgYA0KDQpDcmVhdGUgYSBjb25mdXNpb24gbWF0cml4IGZyb20gdGhlIHByZWRpY3Rpb25zIGFuZCBhY3R1YWwgdmFsdWVzDQoNCmBgYHtyfQ0KY29uZl9tYXRyaXggPC0gdGFibGUoUHJlZGljdGVkID0gcHJlZGljdGlvbnMsIEFjdHVhbCA9IEhvYmJ5LnRlc3QkYFByZWRpY3RlZCBIb2JieWApDQpgYGANCg0KQ2FsY3VsYXRlIGFuZCBwcmludCB0aGUgYWNjdXJhY3kgb2YgdGhlIG1vZGVsDQoNCmBgYHtyfQ0KYWNjdXJhY3kgPC0gc3VtKGRpYWcoY29uZl9tYXRyaXgpKSAvIHN1bShjb25mX21hdHJpeCkNCnByaW50KHBhc3RlKCdBY2N1cmFjeSBvbiB0ZXN0IGRhdGEgaXM6JywgYWNjdXJhY3kpKQ0KYGBgDQoNCkluaXRpYWxpemUgdmVjdG9ycyB0byBob2xkIHRoZSBtZXRyaWNzIGZvciBlYWNoIGNsYXNzDQoNCmBgYHtyfQ0KcHJlY2lzaW9uIDwtIG51bWVyaWMobGVuZ3RoID0gbnJvdyhjb25mX21hdHJpeCkpDQpyZWNhbGwgPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCnNwZWNpZmljaXR5IDwtIG51bWVyaWMobGVuZ3RoID0gbnJvdyhjb25mX21hdHJpeCkpDQpgYGANCg0KQ2FsY3VsYXRlIG1ldHJpY3MgZm9yIGVhY2ggY2xhc3MNCg0KYGBge3J9DQpmb3IgKGkgaW4gMTpucm93KGNvbmZfbWF0cml4KSkgew0KICBUUCA8LSBjb25mX21hdHJpeFtpLCBpXQ0KICBGUCA8LSBzdW0oY29uZl9tYXRyaXhbLCBpXSkgLSBUUA0KICBGTiA8LSBzdW0oY29uZl9tYXRyaXhbaSwgXSkgLSBUUA0KICBUTiA8LSBzdW0oY29uZl9tYXRyaXgpIC0gVFAgLSBGUCAtIEZODQogIA0KICBwcmVjaXNpb25baV0gPC0gVFAgLyAoVFAgKyBGUCkNCiAgcmVjYWxsW2ldIDwtIFRQIC8gKFRQICsgRk4pDQogIHNwZWNpZmljaXR5W2ldIDwtIFROIC8gKFROICsgRlApDQp9DQpgYGANCg0KQXZlcmFnZSB0aGUgbWV0cmljcyBpZiB5b3Ugd2FudCBhIHNpbmdsZSBwZXJmb3JtYW5jZSBtZWFzdXJlDQoNCmBgYHtyfQ0KYXZnX3ByZWNpc2lvbiA8LSBtZWFuKHByZWNpc2lvbikNCmF2Z19yZWNhbGwgPC0gbWVhbihyZWNhbGwpDQphdmdfc3BlY2lmaWNpdHkgPC0gbWVhbihzcGVjaWZpY2l0eSkNCmBgYA0KDQpPdXRwdXQgdGhlIGV2YWx1YXRpb24gbWV0cmljcw0KDQpgYGB7cn0NCnByaW50KHBhc3RlKCJPdmVyYWxsIEFjY3VyYWN5OiIsIGFjY3VyYWN5KSkNCnByaW50KHBhc3RlKCJBdmVyYWdlIFByZWNpc2lvbjoiLCBhdmdfcHJlY2lzaW9uKSkNCnByaW50KHBhc3RlKCJBdmVyYWdlIFJlY2FsbCAoU2Vuc2l0aXZpdHkpOiIsIGF2Z19yZWNhbGwpKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgU3BlY2lmaWNpdHk6IiwgYXZnX3NwZWNpZmljaXR5KSkNCmBgYA0KDQpwcmludCB0aGUgbWV0cmljcyBmb3IgZWFjaCBjbGFzczoNCg0KYGBge3J9DQoNCm1ldHJpY3MgPC0gZGF0YS5mcmFtZSgNCiAgQ2xhc3MgPSByb3duYW1lcyhjb25mX21hdHJpeCksDQogIFByZWNpc2lvbiA9IHByZWNpc2lvbiwNCiAgUmVjYWxsID0gcmVjYWxsLA0KICBTcGVjaWZpY2l0eSA9IHNwZWNpZmljaXR5DQopDQpgYGANCg0KUHJpbnQgbWV0cmljcw0KDQpgYGB7cn0NCnByaW50KG1ldHJpY3MpDQpgYGANCg0KR2VuZXJhdGUgYW5kIHByaW50IGFkZGl0aW9uYWwgcGVyZm9ybWFuY2UgbWV0cmljcyB1c2luZyBjYXJldCBwYWNrYWdlDQoNCmBgYHtyfQ0KY29uZnVzaW9uTWF0cml4KHByZWRpY3Rpb25zLCBIb2JieS50ZXN0JGBQcmVkaWN0ZWQgSG9iYnlgKQ0KYGBgDQoNClBsb3QgdGhlIGRlY2lzaW9uIHRyZWUNCg0KYGBge3J9DQpwbG90KG1vZGVsKQ0KYGBgDQoNCioqRGVjaXNpb24gVHJlZSBBbmFseXNpcyBVc2luZyBHYWluIFJhdGlvKDgwLzIwKToqKg0KDQpUaGUgZGVjaXNpb24gdHJlZSBkZXBpY3RlZCBjbGFzc2lmaWVzIGhvYmJpZXMgaW50byAnQWNhZGVtaWNzJyAoMSksICdBcnRzJyAoMiksIGFuZCAnU3BvcnRzJyAoMykuIEl0IHN0YXJ0cyB3aXRoICdDYXJlZXJfc3BydCcgYSB2YWx1ZSBvZiAwIGxlYWRzIHRvICdXb25fYXJ0cycuIElmICdXb25fYXJ0cycgaXMgMCBvciAyLCB0aGUgbW9kZWwgc3VnZ2VzdHMgJ0FjYWRlbWljcycgb3IgJ0FydHMnLiBJZiAnQ2FyZWVyX3NwcnQnIGlzIDEsICdGYW50X2FydHMnIGlzIGNvbnNpZGVyZWQgbmV4dDsgYSB2YWx1ZSBvZiAwIGFmdGVyICdXb25fYXJ0cycgYmVpbmcgMSBwb2ludHMgdG93YXJkcyAnQXJ0cycsIHdoaWxlIGEgdmFsdWUgb2YgMSBsZWFkcyB0byAnT2x5bXBpYWRfUGFydGljaXBhdGlvbicsIHdoaWNoLCBpZiAxLCBpbmRpY2F0ZXMgJ0FjYWRlbWljcycuIENvbnZlcnNlbHksIGEgaGlnaCAnR3Jhc3BfcG93JyAoXD40KSBwcmVkaWN0cyAnU3BvcnRzJy4NCg0KDQoNCioqMy1HYWluIHJhdGlvKDcwJSwzMCUpKioNCg0KSW5zdGFsbCBuZWNlc3NhcnkgbGlicmFyaWVzDQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygiQzUwIikNCmluc3RhbGwucGFja2FnZXMoInByaW50ciIpDQppbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQpgYGANCg0KTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzDQoNCmBgYHtyfQ0KDQpsaWJyYXJ5KEM1MCkNCmxpYnJhcnkocHJpbnRyKQ0KbGlicmFyeShjYXJldCkNCmBgYA0KDQpTZXQgYSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCg0KYGBge3J9DQpzZXQuc2VlZCgxOTU4KQ0KDQpgYGANCg0KU3BsaXR0aW5nIHRoZSBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3Qgc2V0cw0KDQpgYGB7cn0NCnRyYWluX2luZGljZXMgPC0gc2FtcGxlKDE6bnJvdyhIb2JieV9EYXRhKSwgMC43ICogbnJvdyhIb2JieV9EYXRhKSkNCkhvYmJ5LnRyYWluIDwtIEhvYmJ5X0RhdGFbdHJhaW5faW5kaWNlcywgXQ0KSG9iYnkudGVzdCA8LSBIb2JieV9EYXRhWy10cmFpbl9pbmRpY2VzLCBdDQoNCmBgYA0KDQpUcmFpbmluZyB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbA0KDQpgYGB7cn0NCm1vZGVsIDwtIEM1LjAoYFByZWRpY3RlZCBIb2JieWAgfiAuLCBkYXRhID0gSG9iYnkudHJhaW4sIGNvbnRyb2wgPSBDNS4wQ29udHJvbChDRiA9IDAuMDEpKQ0KDQpgYGANCg0KTWFraW5nIHByZWRpY3Rpb25zIG9uIHRoZSB0ZXN0IHNldA0KDQpgYGB7cn0NCnByZWRpY3Rpb25zIDwtIHByZWRpY3QobW9kZWwsIG5ld2RhdGEgPSBIb2JieS50ZXN0LCB0eXBlID0gJ2NsYXNzJykNCmBgYA0KDQpDcmVhdGUgYSBjb25mdXNpb24gbWF0cml4IGZyb20gdGhlIHByZWRpY3Rpb25zIGFuZCBhY3R1YWwgdmFsdWVzDQoNCmBgYHtyfQ0KY29uZl9tYXRyaXggPC0gdGFibGUoUHJlZGljdGVkID0gcHJlZGljdGlvbnMsIEFjdHVhbCA9IEhvYmJ5LnRlc3QkYFByZWRpY3RlZCBIb2JieWApDQpgYGANCg0KQ2FsY3VsYXRlIGFuZCBwcmludCB0aGUgYWNjdXJhY3kgb2YgdGhlIG1vZGVsDQoNCmBgYHtyfQ0KYWNjdXJhY3kgPC0gc3VtKGRpYWcoY29uZl9tYXRyaXgpKSAvIHN1bShjb25mX21hdHJpeCkNCnByaW50KHBhc3RlKCdBY2N1cmFjeSBvbiB0ZXN0IGRhdGEgaXM6JywgYWNjdXJhY3kpKQ0KYGBgDQoNCkluaXRpYWxpemUgdmVjdG9ycyB0byBob2xkIHRoZSBtZXRyaWNzIGZvciBlYWNoIGNsYXNzDQoNCmBgYHtyfQ0KcHJlY2lzaW9uIDwtIG51bWVyaWMobGVuZ3RoID0gbnJvdyhjb25mX21hdHJpeCkpDQpyZWNhbGwgPC0gbnVtZXJpYyhsZW5ndGggPSBucm93KGNvbmZfbWF0cml4KSkNCnNwZWNpZmljaXR5IDwtIG51bWVyaWMobGVuZ3RoID0gbnJvdyhjb25mX21hdHJpeCkpDQpgYGANCg0KQ2FsY3VsYXRlIG1ldHJpY3MgZm9yIGVhY2ggY2xhc3MNCg0KYGBge3J9DQpmb3IgKGkgaW4gMTpucm93KGNvbmZfbWF0cml4KSkgew0KICBUUCA8LSBjb25mX21hdHJpeFtpLCBpXQ0KICBGUCA8LSBzdW0oY29uZl9tYXRyaXhbLCBpXSkgLSBUUA0KICBGTiA8LSBzdW0oY29uZl9tYXRyaXhbaSwgXSkgLSBUUA0KICBUTiA8LSBzdW0oY29uZl9tYXRyaXgpIC0gVFAgLSBGUCAtIEZODQogIA0KICBwcmVjaXNpb25baV0gPC0gVFAgLyAoVFAgKyBGUCkNCiAgcmVjYWxsW2ldIDwtIFRQIC8gKFRQICsgRk4pDQogIHNwZWNpZmljaXR5W2ldIDwtIFROIC8gKFROICsgRlApDQp9DQpgYGANCg0KQXZlcmFnZSB0aGUgbWV0cmljcyBpZiB5b3Ugd2FudCBhIHNpbmdsZSBwZXJmb3JtYW5jZSBtZWFzdXJlDQoNCmBgYHtyfQ0KYXZnX3ByZWNpc2lvbiA8LSBtZWFuKHByZWNpc2lvbikNCmF2Z19yZWNhbGwgPC0gbWVhbihyZWNhbGwpDQphdmdfc3BlY2lmaWNpdHkgPC0gbWVhbihzcGVjaWZpY2l0eSkNCmBgYA0KDQpPdXRwdXQgdGhlIGV2YWx1YXRpb24gbWV0cmljcw0KDQpgYGB7cn0NCnByaW50KHBhc3RlKCJPdmVyYWxsIEFjY3VyYWN5OiIsIGFjY3VyYWN5KSkNCnByaW50KHBhc3RlKCJBdmVyYWdlIFByZWNpc2lvbjoiLCBhdmdfcHJlY2lzaW9uKSkNCnByaW50KHBhc3RlKCJBdmVyYWdlIFJlY2FsbCAoU2Vuc2l0aXZpdHkpOiIsIGF2Z19yZWNhbGwpKQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgU3BlY2lmaWNpdHk6IiwgYXZnX3NwZWNpZmljaXR5KSkNCmBgYA0KDQpwcmludCB0aGUgbWV0cmljcyBmb3IgZWFjaCBjbGFzczoNCg0KYGBge3J9DQoNCm1ldHJpY3MgPC0gZGF0YS5mcmFtZSgNCiAgQ2xhc3MgPSByb3duYW1lcyhjb25mX21hdHJpeCksDQogIFByZWNpc2lvbiA9IHByZWNpc2lvbiwNCiAgUmVjYWxsID0gcmVjYWxsLA0KICBTcGVjaWZpY2l0eSA9IHNwZWNpZmljaXR5DQopDQpgYGANCg0KUHJpbnQgbWV0cmljcw0KDQpgYGB7cn0NCnByaW50KG1ldHJpY3MpDQpgYGANCg0KR2VuZXJhdGUgYW5kIHByaW50IGFkZGl0aW9uYWwgcGVyZm9ybWFuY2UgbWV0cmljcyB1c2luZyBjYXJldCBwYWNrYWdlDQoNCmBgYHtyfQ0KY29uZnVzaW9uTWF0cml4KHByZWRpY3Rpb25zLCBIb2JieS50ZXN0JGBQcmVkaWN0ZWQgSG9iYnlgKQ0KYGBgDQoNClBsb3QgdGhlIGRlY2lzaW9uIHRyZWUNCg0KYGBge3J9DQpwbG90KG1vZGVsKQ0KYGBgDQoNCioqRGVjaXNpb24gVHJlZSBBbmFseXNpcyBVc2luZyBHYWluIFJhdGlvKDcwJS8zMCUpOioqDQoNCkluIFRoaXJkIFRyZWUgLHdlIGRldmlkZSBkYXRhc2V0IGludG8gdHJhaW5pbmcgc2V0IGFuZCB0ZXN0IHNldCB3aXRoIHNpemUoJTcwLCUzMCkgcmVzcGVjdGl2ZWx5LiBBcyB5b3UgY2FuIHNlZSBpbiB0aGUgZmlndXJlLCB0aGUgcm9vdCBub2RlIGlzICJDYXJlZXJfc3BydCIgLCBjbGFzcyAxKCJBY2FkZW1pY3MiKSwgY2xhc3MgMigiQXJ0cyIpLCBhbmQgY2xhc3MgMyAoIlNwb3J0cyIpLg0KDQpUaGUgZmlyc3QgZGVjaXNpb24gaXMgYmFzZWQgb24gd2hldGhlciB0aGUgdmFsdWUgb2YgdGhlICJDYXJlZXJfc3BydCIgYXR0cmlidXRlIGlzIDAuSWYgIkNhcmVlcl9zcHJ0IiBpcyAwLCB0aGVuIGNoZWNrIGlmICJXb25fYXJ0cyIgaXMgZWl0aGVyIDAgb3IgMi5wcmVkaWN0ICJBY2FkZW1pY3MiLklmICJXb25fYXJ0cyIgaXMgMSwgdGhlbiBjaGVjayBpZiAiRmFudF9hcnRzIiBpcyAxLHByZWRpY3QgIkFydHMiLklmICIiRmFudF9hcnRzIGlzIDAgYW5kIldvbl9hcnRzIiBpcyAwIG9yIDIsIHRoZW4gY2hlY2sgaWYgIlRpbWVfYXJ0IiBpcyBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gMixwcmVkaWN0ICJBY2FkZW1pY3MiLiBJZiAiVGltZV9hcnQiIGlzIGdyZWF0ZXIgdGhhbiAyLCBwcmVkaWN0ICJBcnRzIi4NCg0KSWYgIkNhcmVlcl9zcHJ0ImlzIDEsIHRoZW4gY2hlY2sgaWYgIkZhbnRfYXJ0cyIgaXMgMCwgcHJlZGljdCAiU3BvcnRzIi5JZiAiRmFudF9hcnRzImlzIDEsIHRoZW4gY2hlY2sgaWYgIlRpbWVfYXJ0IiBpcyBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gMiBjaGVjayBpZiAiQWN0X3NwcnQiIGlzIDAscHJlZGljdCAiQWNhZGVtaWNzIiAuSWYgIkFjdF9zcHJ0IiBpcyAxIHByZWRpY3QgIlNwb3J0cyIuIGlmICJUaW1lX2FydCIgaXMgZ3JlYXRlciB0aGFuIDIsIHRoZW4gY2hlY2sgaWYgIldvbl9hcnRzIiBpcyAwIHByZWRpY3QgIlNwb3J0cyIuSWYgIldvbl9hcnRzIiBpcyAxIG9yIDIgcHJlZGljdCAiQXJ0cyIuDQoNCioqQ29tcGFyaW5nIERlY2lzaW9uIFRyZWUgUmVzdWx0cyBVc2luZyBHYWluIFJhdGlvKioNCg0KQWNyb3NzIFRocmVlIFRyYWluaW5nLVRlc3QgU2l6ZXM6IFRoZSBhY2N1cmFjeSByYXRlcyAtMC44OTQ0IGZvciB0aGUgOTA6MTAgc3BsaXQsIDAuODkzOSBmb3IgdGhlIDcwOjMwIHNwbGl0LCBhbmQgMC44ODc5IGZvciB0aGUgODA6MjAgc3BsaXQgLS0gaW5kaWNhdGUgb25seSBzbGlnaHQgdmFyaWF0aW9ucywgd2l0aCB0aGUgOTA6MTAgc3BsaXQgYmVpbmcgbWFyZ2luYWxseSBiZXR0ZXIuDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KKiotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tQ2x1c3RlcmluZy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qKg0KDQoNCg0KYGBge3J9DQpzdHIoSG9iYnlfcHJvYykNCmBgYA0KDQoqKlRvIGNvbnZlcnQgdHlwZSBmcm9tIGZhY3RvciBjb2x1bW5zIHRvIG51bWVyaWMqKg0KDQpgYGB7cn0NCg0KSG9iYnlfcHJvYyRGYXZfc3ViIDwtYXMubnVtZXJpYyhIb2JieV9wcm9jJEZhdl9zdWIpDQoNCkhvYmJ5X3Byb2MkT2x5bXBpYWRfUGFydGljaXBhdGlvbiA8LWFzLm51bWVyaWMoSG9iYnlfcHJvYyRPbHltcGlhZF9QYXJ0aWNpcGF0aW9uKQ0KDQpIb2JieV9wcm9jJFByb2plY3RzIDwtYXMubnVtZXJpYyhIb2JieV9wcm9jJFByb2plY3RzKQ0KDQpIb2JieV9wcm9jJFNjaG9sYXJzaGlwIDwtYXMubnVtZXJpYyhIb2JieV9wcm9jJFNjaG9sYXJzaGlwKQ0KDQpIb2JieV9wcm9jJENhcmVlcl9zcHJ0IDwtYXMubnVtZXJpYyhIb2JieV9wcm9jJENhcmVlcl9zcHJ0KQ0KDQpIb2JieV9wcm9jJEFjdF9zcHJ0IDwtYXMubnVtZXJpYyhIb2JieV9wcm9jJEFjdF9zcHJ0KQ0KDQpIb2JieV9wcm9jJEZhbnRfYXJ0cyA8LWFzLm51bWVyaWMoSG9iYnlfcHJvYyRGYW50X2FydHMpDQoNCkhvYmJ5X3Byb2MkV29uX2FydHMgPC1hcy5udW1lcmljKEhvYmJ5X3Byb2MkV29uX2FydHMpDQoNCkhvYmJ5X3Byb2MkYFByZWRpY3RlZCBIb2JieWAgPC1hcy5udW1lcmljKEhvYmJ5X3Byb2MkYFByZWRpY3RlZCBIb2JieWAgKQ0KYGBgDQoNCg0KYGBge3J9DQpzdHIoSG9iYnlfcHJvYykNCmBgYA0KDQoqKlNjYWxlZCBBbGwgQ29sdW1ucyoqDQoNCmBgYHtyfQ0KSG9iYnlfcHJvYyA8LXNjYWxlKEhvYmJ5X3Byb2MpDQpgYGANCg0KKipEYXRhIHNldCB3aXRob3V0IGdyb3VuZCB0cnV0aCoqDQoNCmBgYHtyfQ0KSG9iYnlfRGF0YTIgPC0gSG9iYnlfcHJvY1ssICEoY29sbmFtZXMoSG9iYnlfcHJvYykgJWluJSBjKCJQcmVkaWN0IEhvYmJ5IikpXQ0KYGBgDQoNCg0KT3VyIGRhdGEgc2V0IGV4aGliaXRzIGEgYmFsYW5jZWQgZGlzdHJpYnV0aW9uIGFtb25nIGNsYXNzIGxhYmVscywgd2l0aA0KImFjYWRlbWljLCIgImFydCwiIGFuZCAic3BvcnQiIGNvbnN0aXR1dGluZyA0My42JSwgMjUuNiUsIGFuZCAzMC44JSBvZg0KdGhlIHRvdGFsLCByZXNwZWN0aXZlbHkuIFRoaXMgYmFsYW5jZWQgZGlzdHJpYnV0aW9uIGlzIGFkdmFudGFnZW91cyBmb3INCmJvdGggY2xhc3NpZmljYXRpb24gYW5kIGNsdXN0ZXJpbmcgdGFza3MuIEluIGNsYXNzaWZpY2F0aW9uLCBhIGJhbGFuY2VkDQpkYXRhIHNldCBoZWxwcyBwcmV2ZW50IHRoZSBtb2RlbCBmcm9tIGZhdm9yaW5nIG9uZSBjbGFzcyBvdmVyIHRoZQ0Kb3RoZXJzLCBlbnN1cmluZyB0aGF0IHRoZSBsZWFybmluZyBhbGdvcml0aG0gaXMgZXhwb3NlZCB0byBhDQpyZXByZXNlbnRhdGl2ZSBzZXQgb2YgZXhhbXBsZXMgZnJvbSBlYWNoIGNhdGVnb3J5LiBUaGlzIGJhbGFuY2UgcHJvbW90ZXMNCnRoZSBkZXZlbG9wbWVudCBvZiBhIG1vZGVsIHRoYXQgZ2VuZXJhbGl6ZXMgd2VsbCBhY3Jvc3MgYWxsIGNsYXNzZXMsDQplbmhhbmNpbmcgaXRzIHByZWRpY3RpdmUgcGVyZm9ybWFuY2Ugb24gbmV3LCB1bnNlZW4gZGF0YS4gSW4gY2x1c3RlcmluZywNCmEgYmFsYW5jZWQgZGF0YSBzZXQgYWlkcyBpbiBmb3JtaW5nIGNsdXN0ZXJzIHRoYXQgYXJlIG1vcmUgZXZlbmx5DQpkaXN0cmlidXRlZCwgYWxsb3dpbmcgZm9yIGEgY29tcHJlaGVuc2l2ZSB1bmRlcnN0YW5kaW5nIG9mIHBhdHRlcm5zIGFuZA0KcmVsYXRpb25zaGlwcyBhY3Jvc3MgZGl2ZXJzZSBjYXRlZ29yaWVzLiBCYWxhbmNlZCBkYXRhIHNldHMgb2Z0ZW4gbGVhZA0KdG8gbW9yZSBhY2N1cmF0ZSBhbmQgZmFpciBjbHVzdGVyaW5nIHJlc3VsdHMsIGVuYWJsaW5nIG1lYW5pbmdmdWwNCmluc2lnaHRzIGludG8gdGhlIHVuZGVybHlpbmcgc3RydWN0dXJlcyB3aXRoaW4gZWFjaCBjbGFzcy4NCg0KKipyZXF1aXJlIHBhY2thZ2VzL0xpYnJhcnkqKg0KDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKSANCmluc3RhbGwucGFja2FnZXMoIm1hZ3JpdHRyIikNCmluc3RhbGwucGFja2FnZXMoImRwbHlyIikNCmxpYnJhcnkoZmFjdG9leHRyYSkgDQpsaWJyYXJ5KGNsdXN0ZXIpDQpsaWJyYXJ5KGRwbHlyKQ0KYGBgDQoNCg0KDQoqKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLWstbWVhbnNjbHVzdGVyaW5nLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKioNCg0KKipWYWxpZGF0aW9uOioqDQoNCkRldGVybWluaW5nIHRoZSByaWdodCBudW1iZXIgb2YgY2x1c3RlcnMgYmVmb3JlIHN0YXJ0aW5nIHRoZSBjbHVzdGVyaW5nDQpwcm9jZXNzIGlzIGxpa2UgbWFraW5nIHN1cmUgeW91IGhhdmUgdGhlIGNvcnJlY3Qtc2l6ZWQgcHV6emxlIHBpZWNlcw0KYmVmb3JlIHB1dHRpbmcgdGhlIHB1enpsZSB0b2dldGhlci4gSXQncyBpbXBvcnRhbnQgYmVjYXVzZSBzb21lDQpjbHVzdGVyaW5nIGFsZ29yaXRobXMsIGxpa2UgSy1tZWFucywgcmVxdWlyZSBzdWNoIGEgcGFyYW1ldGVyLiBJbg0KYWRkaXRpb24gdG8gdGhhdCwgaXQgaGVscHMgbWFrZSB0aGUgY2x1c3RlcmluZyBtb3JlIGFjY3VyYXRlIGFuZCB1c2VmdWwuDQpJZiB5b3Uga25vdyB0aGUgcmlnaHQgbnVtYmVyIGJlZm9yZWhhbmQsIGl0IHNhdmVzIHRpbWUgYW5kIGhlbHBzIG1ha2UNCnRoZSB3aG9sZSBwcm9jZXNzIG1vcmUgZWZmaWNpZW50IGFuZCB0aGUgcmVzdWx0cyBtb3JlIHJlbGlhYmxlLg0KDQoqKmNvbXB1dGUgYXZlcmFnZSBzaWxob3VldHRlIGZvciBrIGNsdXN0ZXJzIHVzaW5nIHNpbGhvdWV0dGUoKSBGb3Igay1tZWFuKioNCg0KYGBge3J9DQpzaWxob3VldHRlX3Njb3JlIDwtIGZ1bmN0aW9uKGspIHsNCiAga20gPC0ga21lYW5zKEhvYmJ5X0RhdGEyLCBjZW50ZXJzID0gaywgbnN0YXJ0ID0gMjUpDQogIHNzIDwtIHNpbGhvdWV0dGUoa20kY2x1c3RlciwgZGlzdChIb2JieV9EYXRhMikpDQogIHNpbCA8LSBtZWFuKHNzWywgM10pDQogIHJldHVybihzaWwpDQp9DQoNCiMgayBjbHVzdGVyIHJhbmdlIGZyb20gMiB0byAxMA0KayA8LSAyOjEwDQoNCiMgY2FsbCBmdW5jdGlvbiBmb3IgZWFjaCBrIHZhbHVlDQphdmdfc2lsIDwtIHNhcHBseShrLCBzaWxob3VldHRlX3Njb3JlKQ0KDQojIHBsb3QgdGhlIHJlc3VsdHMNCnBsb3QoaywgYXZnX3NpbCwgdHlwZSA9ICdiJywgeGxhYiA9ICdOdW1iZXIgb2YgY2x1c3RlcnMnLCB5bGFiID0gJ0F2ZXJhZ2UgU2lsaG91ZXR0ZSBTY29yZXMnLCBmcmFtZSA9IEZBTFNFKQ0KYGBgDQoNClRoZSByZXN1bHRzIGluZGljYXRlIHRoYXQgdGhlIGhpZ2hlc3QgYXZlcmFnZSBzaWxob3VldHRlIHNjb3JlcyBpbmRpY2F0ZQ0KdGhlIHF1YWxpdHkgb2YgdGhlIGNsdXN0ZXJzIGFuZCBzdWdnZXN0IGJldHRlci1kZWZpbmVkIGFuZCBtb3JlDQpzZXBhcmF0ZWQgY2x1c3RlcnMsIHdpdGggZWFjaCBwb2ludCBoYXZpbmcgYSBoaWdoIGRlZ3JlZSBvZiBzaW1pbGFyaXR5DQp0byBpdHMgb3duIGNsdXN0ZXIgYW5kIGEgbG93ZXIgc2ltaWxhcml0eSB0byBuZWlnaGJvcmluZyBjbHVzdGVycy4gVGhlDQpoaWdoZXN0IGF2ZXJhZ2Ugc2lsaG91ZXR0ZSBzY29yZXMgd2VyZSBvYnNlcnZlZCBmb3IgayB2YWx1ZXMgb2YgMywgMiwNCmFuZCA0LCBzbyB0aGVzZSBhcmUgdGhlIG9wdGltYWwgbnVtYmVycy4gVGhlc2UgdmFsdWVzIHdpbGwgYmUgZW1wbG95ZWQNCmluIHN1YnNlcXVlbnQgay1tZWFucyBjbHVzdGVyaW5nIGFuYWx5c2VzLg0KDQoqKmstbWVhbnMgY2x1c3RlciBrPTMqKg0KDQpgYGB7cn0NCiNzZXQgYSBzZWVkIGZvciByYW5kb20gbnVtYmVyIGdlbmVyYXRpb24gIHRvIG1ha2UgdGhlIHJlc3VsdHMgcmVwcm9kdWNpYmxlDQpzZXQuc2VlZCg3KQ0Ka21lYW5zLnJlc3VsdCA8LSBrbWVhbnMoSG9iYnlfRGF0YTIsIDMpDQoNCiMgcHJpbnQgdGhlIGNsdXN0ZXJuZyByZXN1bHQNCmttZWFucy5yZXN1bHQNCg0KI3Zpc3VhbGl6ZSBjbHVzdGVyaW5nDQpmdml6X2NsdXN0ZXIoa21lYW5zLnJlc3VsdCwgZGF0YSA9IEhvYmJ5X0RhdGEyKQ0KYGBgDQoNCioqQXZlcmFnZSBmb3IgZWFjaCBjbHVzdGVyKioNCg0KYGBge3J9DQphdmdfc2lsIDwtIHNpbGhvdWV0dGUoa21lYW5zLnJlc3VsdCRjbHVzdGVyLGRpc3QoSG9iYnlfRGF0YTIpKSANCmZ2aXpfc2lsaG91ZXR0ZShhdmdfc2lsKQ0KYGBgDQoNCnRoZSBwcmVzZW5jZSBvZiBuZWdhdGl2ZSBzaWxob3VldHRlcyBmb3Igc29tZSBvYnNlcnZhdGlvbnMgd2l0aGluIGVhY2gNCmNsdXN0ZXIgaW5kaWNhdGVzIHRoYXQgdGhlc2UgcG9pbnRzIG1pZ2h0IGJlIG1vcmUgc2ltaWxhciB0byBwb2ludHMgaW4NCm90aGVyIGNsdXN0ZXJzLCBzdWdnZXN0aW5nIGEgcG90ZW50aWFsIG92ZXJsYXAgb3IgYW1iaWd1aXR5IGluIHRoZWlyDQphc3NpZ25tZW50LiBUaGUgZmFjdCB0aGF0IHNvbWUgb2JzZXJ2YXRpb25zIGhhdmUgbmVnYXRpdmUgc2lsaG91ZXR0ZXMNCmhpZ2hsaWdodHMgdGhhdCB0aGUgc2VwYXJhdGlvbiBvZiBkYXRhIHBvaW50cyBpcyBub3QgZW50aXJlbHkNCnN1ZmZpY2llbnQuDQoNClNvIHNpbmNlIDMsIHdoaWNoIHdhcyB0aGUgb3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlcnMgd2l0aCB0aGUgaGlnaGVzdA0Kc2lsaG91ZXR0ZSBzY29yZSBhdmVyYWdlLCBkaWQgbm90IGhhdmUgZ29vZCBjbHVzdGVyaW5nIHJlc3VsdHMsIHRoYXQNCmRvZXMgc3VwcG9ydCBvdXIgcmVzZWFyY2ggcmVzdWx0cyB0aGF0IHRoZSBrLW1lYW5zIGFsZ29yaXRobSBpcyBub3QNCmFwcGxpY2FibGUgdG8gY2F0ZWdvcmljYWxkYXRhIGNsdXN0ZXJpbmcgYmVjYXVzZSBpdCByZWxpZXMgb24gdGhlDQpFdWNsaWRlYW4gZGlzdGFuY2UgbWV0cmljIHRvbWVhc3VyZSB0aGUgc2ltaWxhcml0eSBiZXR3ZWVuIGRhdGEgcG9pbnRzLg0KSG93ZXZlciwgZXZlbiBhZnRlckVuY29kaW5nIGFuZCBpdHMgYXBwbGljYXRpb24gdG8gY2F0ZWdvcmljYWwgZGF0YSBwb3NlDQpzaWduaWZpY2FudCBjaGFsbGVuZ2VzLiBDYXRlZ29yaWNhbCB2YXJpYWJsZXMgb2Z0ZW4gbGFjayBhIG1lYW5pbmdmdWwNCm51bWVyaWNhbHJlcHJlc2VudGF0aW9uOyBmb3IgaW5zdGFuY2UsIHRha2luZyB0aGUgbWVhbiBvZiBjYXRlZ29yaWVzDQpsaWtlIHRoZWZlYXR1cmUgImZhdm9yaXRlIHN1YmplY3QsIiBha2EgRmF2X3N1YiIgKGV2ZW4gYWZ0ZXIgZW5jb2RpbmcpLA0KbWlnaHQgbm90aGF2ZSBhbnkgcHJhY3RpY2FsIGludGVycHJldGF0aW9uLiBBbmQgdGhlIGRpc3RhbmNlcyBjYWxjdWxhdGVkDQppbiB0aGVhbGdvcml0aG0gbWF5IG5vdCByZWZsZWN0IHRoZSB0cnVlIGRpc3NpbWlsYXJpdGllcyBiZXR3ZWVuDQpjYXRlZ29yaWNhbHZhbHVlcy4gVGhlIGVuY29kaW5nIHByb2Nlc3MgaXRzZWxmIGludHJvZHVjZXMgYXJ0aWZpY2lhbA0KbnVtZXJpY2FscmVsYXRpb25zaGlwcyB0aGF0IG1heSBtaXNsZWFkIHRoZSBhbGdvcml0aG0uIE1vcmVvdmVyLCBrLW1lYW5zDQpyZWxpZXNvbiB0aGUgbWluaW1pemF0aW9uIG9mIEV1Y2xpZGVhbiBkaXN0YW5jZXMsIHdoaWNoIG1pZ2h0IG5vdA0KYWNjdXJhdGVseWNhcHR1cmUgdGhlIGRpc3NpbWlsYXJpdHkgc3RydWN0dXJlIGluIGNhdGVnb3JpY2FsIGRhdGEuDQpDYXRlZ29yaWNhbHZhcmlhYmxlcyBpbmhlcmVudGx5IGV4aGliaXQgZGlzY3JldGUgYW5kIG5vbi1vcmRpbmFsDQpjaGFyYWN0ZXJpc3RpY3N0aGF0IGFyZSBub3Qgd2VsbC1zdWl0ZWQgZm9yIHRoZSBjb250aW51b3VzIGFuZCBsaW5lYXINCmFzc3VtcHRpb25zIG9may1tZWFucy4gQWx0ZXJuYXRpdmUgY2x1c3RlcmluZyB0ZWNobmlxdWVzLCBzcGVjaWZpY2FsbHkNCmRlc2lnbmVkIGZvcmNhdGVnb3JpY2FsIGRhdGEsIHN1Y2ggYXMgcGFydGl0aW9uaW5nIGFyb3VuZCBtZWRvaWRzLCBhcmUNCm1vcmVhcHByb3ByaWF0ZSBmb3IgY2FwdHVyaW5nIHRoZSBpbnRyaW5zaWMgcGF0dGVybnMgYW5kIHJlbGF0aW9uc2hpcHMNCmluY2F0ZWdvcmljYWwgZGF0YXNldHMuDQoNCg0KDQoNCg0KDQoNCioqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1rLW1lZGlvZHMgY2x1c3RlcmluZyB3aXRoIFBBTS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKioNCg0KSy1tZWRvaWRzIGNsdXN0ZXJpbmcgcHJlc2VudHMgYSByb2J1c3QgYWx0ZXJuYXRpdmUgZm9yIGFuYWx5emluZw0KY2F0ZWdvcmljYWwgZGF0YSBieSBhZGRyZXNzaW5nIHRoZSBsaW1pdGF0aW9ucyBwb3NlZCBieSBrLW1lYW5zDQpjbHVzdGVyaW5nLiBVbmxpa2Ugay1tZWFucywgay1tZWRvaWRzIGRvZXMgbm90IHJlbHkgb24gdGhlIG1lYW4gYXMgYQ0KcmVwcmVzZW50YXRpdmUgY2VudHJvaWQgYnV0IGVtcGxveXMgbWVkb2lkcywgd2hpY2ggYXJlIGFjdHVhbCBkYXRhDQpwb2ludHMgd2l0aGluIHRoZSBjbHVzdGVycywgYW5kIHRoZSBhbGdvcml0aG0gZGVmaW5lcyBjbHVzdGVycyBiYXNlZCBvbg0KcGFydGl0aW9uaW5nIGFyb3VuZCBtZWRvaWRzLiBUaGlzIGZlYXR1cmUgbWFrZXMgay1tZWRvaWRzIHBhcnRpY3VsYXJseQ0Kc3VpdGFibGUgZm9yIGNhdGVnb3JpY2FsIGRhdGEsIHdoZXJlIG1lYW5pbmdmdWwgY2VudHJvaWRzIG1heSBub3QgaGF2ZSBhDQpudW1lcmljYWwgaW50ZXJwcmV0YXRpb24uIFRoZSBhbGdvcml0aG0gZGVmaW5lcyBjbHVzdGVycyBiYXNlZCBvbg0KcGFydGl0aW9uaW5nIGFyb3VuZCBtZWRvaWRzLg0KDQoqKlZhbGlkYXRpb246KioNCg0KRmlyc3QsIHdlIHdhbnQgdG8gZGV0ZXJtaW5lIHRocmVlIGRpZmZlcmVudCBudW1iZXJzIG9mIGNsdXN0ZXJzIGJ5IHVzaW5nDQphIG51bWJlciBvZiBtZXRob2RzIHRoYXQgd2lsbCBzdWdnZXN0IHRoZSBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycyBmb3INCmstbWVkaW9kcyBjbHVzdGVyaW5nIHdpdGggUEFNLg0KDQoqKlNpbGhvdWV0dGUgY29lZmZpY2llbnQqKg0KDQpgYGB7cn0NCmZ2aXpfbmJjbHVzdChIb2JieV9EYXRhMiwgcGFtLCBtZXRob2QgPSAic2lsaG91ZXR0ZSIpKw0KICBsYWJzKHN1YnRpdGxlID0gIlNpbGhvdWV0dGUgbWV0aG9kIikNCmBgYA0KDQoqKkVsYm93IG1ldGhvZCoqDQoNCmBgYHtyfQ0KZnZpel9uYmNsdXN0KEhvYmJ5X0RhdGEyLCBwYW0sIG1ldGhvZCA9ICJ3c3MiKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdD0gMywgbGluZXR5cGU9IDMpKw0KICBsYWJzKHN1YnRpdGxlID0gIkVsYm93IG1ldGhvZCIpDQpgYGANCg0KVGhlIHNpbGhvdWV0dGUgY29lZmZpY2llbnQsIHdoaWNoIG1lYXN1cmVzIHRoZSBjb2hlc2lvbiBhbmQgc2VwYXJhdGlvbg0Kb2YgY2x1c3RlcnMsIGFsaWducyB3aXRoIHRoZSBFbGJvdyBtZXRob2QsIHdoaWNoIGFzc2Vzc2VzIHRoZQ0Kd2l0aGluLWNsdXN0ZXIgc3VtIG9mIHNxdWFyZXMgKHdzcykgYXMgYSBmdW5jdGlvbiBvZiB0aGUgbnVtYmVyIG9mDQpjbHVzdGVycyBpbiB0aGUgc3VnZ2VzdGVkIG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXJzLiBUaGlzIGFsaWdubWVudCBpbg0KcmVzdWx0cyBiZXR3ZWVuIHR3byBkaXN0aW5jdCBldmFsdWF0aW9uIG1ldGhvZHMgc3RyZW5ndGhlbnMgY29uZmlkZW5jZQ0KaW4gdGhlIGNob2ljZSBvZiB0aHJlZSBjbHVzdGVycywgcHJvdmlkaW5nIGEgc3RhYmxlIGZvdW5kYXRpb24gZm9yDQpmdXJ0aGVyIGFuYWx5c2lzIGFuZCBpbnRlcnByZXRhdGlvbiBvZiB0aGUgdW5kZXJseWluZyBwYXR0ZXJucyB3aXRoaW4NCnRoZSBkYXRhc2V0LiBJbiBhZGRpdGlvbiB0byB0aGF0LCB0aGUgZ3JvdW5kIHRydXRoIChjbGFzcyBsYWJlbHMpIGFsc28NCmNvbnRhaW5zIHRocmVlIGNsYXNzZXMsIGFuZCB0aGF0IGluZGljYXRlcyBhIHJlYXNzdXJpbmcgYWxpZ25tZW50DQpiZXR3ZWVuIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGEgYW5kIHRoZSBjbHVzdGVyaW5nIHJlc3VsdHMuDQoNCg0KDQpXZSBuZWVkIHRvIGRldGVybWluZSB0d28gbW9yZSBzdWdnZXN0ZWQgbnVtYmVycyBvZiBjbHVzdGVycyBieSBjb21wdXRpbmcNCnRoZSBhdmVyYWdlIHNpbGhvdWV0dGUgZm9yIGsgY2x1c3RlcnMgdXNpbmcgc2lsaG91ZXR0ZSgpLg0KYGBge3J9DQpzaWxob3VldHRlX3Njb3JlIDwtIGZ1bmN0aW9uKGspIHsNCiAga20gPC0gcGFtKEhvYmJ5X0RhdGEyLCBrLCBkaXNzID0gVFJVRSkNCiAgc3MgPC0gc2lsaG91ZXR0ZShrbSRjbHVzdGVyaW5nLCBkaXN0KEhvYmJ5X0RhdGEyKSkNCiAgc2lsIDwtIG1lYW4oc3NbLCAzXSkNCiAgcmV0dXJuKHNpbCkNCn0NCg0KIyBrIGNsdXN0ZXIgcmFuZ2UgZnJvbSAyIHRvIDEwDQprIDwtIDI6MTANCg0KIyBDYWxsIGZ1bmN0aW9uIGZvciBlYWNoIGsgdmFsdWUNCmF2Z19zaWwgPC0gc2FwcGx5KGssIHNpbGhvdWV0dGVfc2NvcmUpDQoNCiMgUGxvdCB0aGUgcmVzdWx0cw0KcGxvdChrLCBhdmdfc2lsLCB0eXBlID0gJ2InLCB4bGFiID0gJ051bWJlciBvZiBjbHVzdGVycycsIHlsYWIgPSAnQXZlcmFnZSBTaWxob3VldHRlIFNjb3JlcycsIGZyYW1lID0gRkFMU0UpDQpgYGANCg0KSXQgaXMgYSBjb21tb24gcHJhY3RpY2UgdG8gY2hvb3NlIHRoZSBudW1iZXIgb2YgY2x1c3RlcnMgY29ycmVzcG9uZGluZw0KdG8gdGhlIHBlYWsgaW4gdGhlIHNpbGhvdWV0dGUgc2NvcmUgcGxvdCwgYW5kIHNpbmNlIHdlIGFyZSBsb29raW5nIGZvcg0KdHdvIG1vcmUgbnVtYmVyIG9mIGNsdXN0ZXJzIG90aGVyIHRoYW4gdGhyZWUsIGl0IHdvdWxkIGJlIHJlYXNvbmFibGUgdG8NCmNvbnNpZGVyIHR3byBhbmQgZm91ciBjbHVzdGVycyBmb3IgZnVydGhlciBhbmFseXNpcy4gRXNwZWNpYWxseSB3aXRoIHRoZQ0KZGVjcmVhc2luZyB0cmVuZCBiZXlvbmQgdGhyZWUgY2x1c3RlcnMsIGl0IGluZGljYXRlcyB0aGF0IGFkZGluZyBtb3JlDQpjbHVzdGVycyBkb2VzIG5vdCBzaWduaWZpY2FudGx5IGltcHJvdmUgdGhlIHNlcGFyYXRpb24gYW5kIGNvaGVzaW9uIG9mDQp0aGUgY2x1c3RlcnMuDQoNCg0KDQoqKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tZ3JvdXAgaW50byBrPTMgY2x1c3RlcnMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSoqDQoNClRoZSBzdWItc2FtcGxpbmcgYW5kIGNsdXN0ZXJpbmcgYXBwcm9hY2ggaXMgYSBoZWxwZnVsIG1ldGhvZCB0byBldmFsdWF0ZQ0KdGhlIHJvYnVzdG5lc3Mgb2YgdGhlIGNsdXN0ZXJpbmcgcmVzdWx0cyB1bmRlciB2YXJpb3VzIHN1YnNldHMgYW5kIHRvDQpvYnRhaW4gaW5zaWdodHMgaW50byB0aGUgc3RydWN0dXJlIG9mIHRoZSBkYXRhLiBGdXJ0aGVybW9yZSwgd2Ugd2lsbA0KdGFrZSAxMDAgc2FtcGxlcyBmcm9tIG91ciBkYXRhIHNldCB0byBlbnN1cmUgdGhhdCB0aGUgY2x1c3RlcmluZyBwbG90DQpkb2Vzbid0IGdldCB0b28gY3Jvd2RlZC4NCg0KYGBge3J9DQpzZXQuc2VlZCg3KQ0KDQojIFNwZWNpZnkgdGhlIG51bWJlciBvZiByb3dzIHlvdSB3YW50IHRvIHNhbXBsZQ0KbnVtX3Jvd3MgPC0gMTAwDQoNCiMgVXNlIHNhbXBsZSB3aXRoIHRoZSBzcGVjaWZpZWQgc2VlZA0KaWR4IDwtIHNhbXBsZSgxOmRpbShIb2JieV9EYXRhMilbMV0sIG51bV9yb3dzKQ0KDQpIb2JieV9EYXRhMyA8LSBIb2JieV9EYXRhMltpZHgsIF0NCg0KcGFtLnJlc3VsdCA8LSBwYW0oSG9iYnlfRGF0YTMsMykNCiNTaG93IHRoZSBzaWxob3V0ZWUgcGxvdCBvZiBQQU0gQU5EIGNsdXN0ZXJzDQpwbG90KHBhbS5yZXN1bHQpDQpgYGANCg0KYGBge3J9DQojIEV4dHJhY3QgdGhlIGNsdXNpbmZvIGNvbXBvbmVudA0KY2x1c2luZm8gPC0gcGFtLnJlc3VsdCRjbHVzaW5mbw0KDQojIENhbGN1bGF0ZSB0aGUgdG90YWwgd2l0aGluLWNsdXN0ZXIgc3VtIG9mIHNxdWFyZXMNCnRvdF93aXRoaW5zcyA8LSBzdW0oY2x1c2luZm9bLCAic2l6ZSJdICogY2x1c2luZm9bLCAiYXZfZGlzcyJdXjIpDQoNCiMgUHJpbnQgdGhlIHJlc3VsdA0KcHJpbnQodG90X3dpdGhpbnNzKQ0KYGBgDQoNCg0KYGBge3J9DQpzdHIoSG9iYnlfRGF0YTIpDQpgYGANCg0KKipEYXRhIHNldCB3aXRob3V0IGdyb3VuZCB0cnV0aCoqDQoNCmBgYHtyfQ0KSG9iYnlfRGF0YTIgPC0gSG9iYnlfcHJvY1ssICEoY29sbmFtZXMoSG9iYnlfcHJvYykgJWluJSBjKCJQcmVkaWN0ZWQgSG9iYnkiKSldDQpgYGANCg0KDQpgYGB7cn0NCiMgQ2hlY2sgdGhlIHN0cnVjdHVyZSBvZiBwYW0ucmVzdWx0JGNsdXN0ZXJpbmcNCnN0cihwYW0ucmVzdWx0JGNsdXN0ZXJpbmcpDQoNCnN1bW1hcnkoSG9iYnlfRGF0YTIpDQpgYGANCg0KDQpgYGB7cn0NCnN0cihIb2JieV9wcm9jKQ0KDQpzdHIoSG9iYnlfRGF0YTIpDQpgYGANCg0KDQpgYGB7cn0NCg0KbGlicmFyeShHR2FsbHkpDQpsaWJyYXJ5KHBsb3RseSkNCg0KDQojIEFzc3VtaW5nICdwYW0ucmVzdWx0JyBpcyB0aGUgcmVzdWx0IGZyb20gUEFNIGNsdXN0ZXJpbmcNCkhvYmJ5X0RhdGEyJGNsdXN0ZXIgPC0gcGFtLnJlc3VsdCRjbHVzdGVyaW5nDQoNCiMgTWFrZSBzdXJlICdjbHVzdGVyJyBpcyBhIGZhY3Rvcg0KSG9iYnlfRGF0YTIkY2x1c3RlciA8LSBhcy5mYWN0b3IoSG9iYnlfRGF0YTIkY2x1c3RlcikNCg0KIyBDcmVhdGUgYSBwYXJhbGxlbCBjb29yZGluYXRlIHBsb3QNCnAgPC0gZ2dwYXJjb29yZCgNCiAgZGF0YSA9IEhvYmJ5X0RhdGEyLA0KICBjb2x1bW5zID0gYygxOjExKSwgICMgQXNzdW1pbmcgdGhlcmUgYXJlIDExIGNvbHVtbnMgaW4geW91ciBkYXRhDQogIGdyb3VwQ29sdW1uID0gImNsdXN0ZXIiLA0KICBzY2FsZSA9ICJzdGQiDQopICsgbGFicyh4ID0gIkhvYmJ5IGNoYXJhY3RlcmlzdGljIiwgeSA9ICJ2YWx1ZSAoaW4gc3RhbmRhcmQtZGV2aWF0aW9uIHVuaXRzKSIsIHRpdGxlID0gIkNsdXN0ZXJpbmciKQ0KDQojIENvbnZlcnQgdGhlIGdncGxvdCBvYmplY3QgdG8gcGxvdGx5DQpwX3Bsb3RseSA8LSBnZ3Bsb3RseShwKQ0KDQojIFByaW50IHRoZSBwbG90DQpwcmludChwX3Bsb3RseSkNCg0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmxpYnJhcnkoR0dhbGx5KQ0KbGlicmFyeShwbG90bHkpDQoNCkhvYmJ5X0RhdGEyJGNsdXN0ZXIgPC0gYXMuZmFjdG9yKHBhbS5yZXN1bHQkY2x1c3RlcikNCg0KcCA8LSBnZ3BhcmNvb3JkKGRhdGEgPSBIb2JieV9EYXRhMiwgY29sdW1ucyA9IGMoMToxMSksIGdyb3VwQ29sdW1uID0gImNsdXN0ZXIiLCBzY2FsZSA9ICJzdGQiKSArIGxhYnMoeCA9ICJtaWxrIGNvbnN0aXR1ZW50IiwgeSA9ICJ2YWx1ZSAoaW4gc3RhbmRhcmQtZGV2aWF0aW9uIHVuaXRzKSIsIHRpdGxlID0gIkNsdXN0ZXJpbmciKQ0KZ2dwbG90bHkocCkNCg0KYGBgDQoNClRoZSBvdXRwdXQgb2YgdGhlIGNvZGUsIGRpc3BsYXlpbmcgYSBzaWxob3VldHRlIHBsb3Qgb2YgdGhlIFBBTQ0KY2x1c3RlcnMsIGluZGljYXRlcyB0aGF0IHRoZSBjbHVzdGVycyBhcmUgcmVsYXRpdmVseSBjbG9zZSB0byBlYWNoDQpvdGhlciwgd2l0aCBhIHNsaWdodCBvdmVybGFwIGJldHdlZW4gdHdvIGNsdXN0ZXJzLiBUaGUgc2lsaG91ZXR0ZSBwbG90DQp2aXN1YWxseSByZXByZXNlbnRzIGhvdyB3ZWxsLWRlZmluZWQgYW5kIHNlcGFyYXRlZCB0aGUgY2x1c3RlcnMgYXJlLg0KV2hpbGUgdGhlIHNsaWdodCBvdmVybGFwIHN1Z2dlc3RzIHRoYXQgdGhlIG5hdHVyYWwgZ3JvdXBpbmcgd2l0aGluIHRoZQ0KZGF0YXNldCBtYXkgbm90IGJlIGVudGlyZWx5IGRpc3RpbmN0LCBpdCBkb2VzIG5vdCBuZWdhdGl2ZWx5IGFmZmVjdCB0aGUNCm92ZXJhbGwgcXVhbGl0eSBvZiB0aGUgY2x1c3RlcnMuIEluIGZhY3QsIHRoZSBvYnNlcnZlZCBvdmVybGFwIG1pZ2h0DQppbmRpY2F0ZSBzaGFyZWQgY2hhcmFjdGVyaXN0aWNzIGJldHdlZW4gYWRqYWNlbnQgY2x1c3RlcnMsIGVmZmVjdGl2ZWx5DQpjYXB0dXJpbmcgbWVhbmluZ2Z1bCBwYXR0ZXJucyBhbmQgZ3JvdXBpbmdzIHRoYXQgcmVmbGVjdCB0aGUgaW50cmljYWNpZXMNCm9mIHJlYWwtd29ybGQgcGhlbm9tZW5hIG5vdCBjb25maW5lZCB0byBzdHJpY3QgYm91bmRhcmllcy4NCg0KYkN1YmVkDQoNCmBgYHtyfQ0KY2x1c3Rlcl9hc3NpZ25tZW50cyA8LSBjKHBhbS5yZXN1bHQkY2x1c3RlcikNCnNldC5zZWVkKDcpDQoNCiMgU3BlY2lmeSB0aGUgbnVtYmVyIG9mIHJvd3MgeW91IHdhbnQgdG8gc2FtcGxlDQpudW1fcm93cyA8LSAxMDANCg0KIyBVc2Ugc2FtcGxlIHdpdGggdGhlIHNwZWNpZmllZCBzZWVkDQppZHggPC0gc2FtcGxlKDE6ZGltKEhvYmJ5X3Byb2MpWzFdLCBudW1fcm93cykNCg0KIyBTZWxlY3QgdGhlIHNhbXBsZWQgcm93cyBmcm9tIEhvYmJ5X3Byb2MNCkhvYmJ5X0RhdGE0IDwtIEhvYmJ5X3Byb2NbaWR4LCBdDQpncm91bmRfdHJ1dGhfbGFiZWxzIDwtIGMoSG9iYnlfRGF0YTQpDQoNCiMgQ3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIGNsdXN0ZXIgYXNzaWdubWVudHMgYW5kIGdyb3VuZCB0cnV0aCBsYWJlbHMNCmRhdGFzZXQgPC0gZGF0YS5mcmFtZShjbHVzdGVyID0gY2x1c3Rlcl9hc3NpZ25tZW50cywgbGFiZWwgPSBncm91bmRfdHJ1dGhfbGFiZWxzKQ0KDQojIENhbGN1bGF0ZSBCQ3ViZWQgcHJlY2lzaW9uIGFuZCByZWNhbGwNCmNhbGN1bGF0ZV9iY3ViZWRfbWV0cmljcyA8LSBmdW5jdGlvbihkYXRhc2V0KSB7DQogIG4gPC0gbnJvdyhkYXRhc2V0KQ0KICBwcmVjaXNpb25fc3VtIDwtIDAgDQogIHJlY2FsbF9zdW0gPC0gMA0KICANCiAgIGZvciAoaSBpbiAxOm4pIHsNCiAgICBjbHVzdGVyIDwtIGRhdGFzZXQkY2x1c3RlcltpXSANCiAgICBsYWJlbCA8LSBkYXRhc2V0JGxhYmVsW2ldDQogICAgDQogICAgIyBDb3VudCB0aGUgbnVtYmVyIG9mIGl0ZW1zIGZyb20gdGhlIHNhbWUgY2F0ZWdvcnkgaW4gaXRzIGNsdXN0ZXINCiAgICBzYW1lX2NhdGVnb3J5IDwtIHN1bShkYXRhc2V0JGxhYmVsW2RhdGFzZXQkY2x1c3RlciA9PSBjbHVzdGVyXSA9PSBsYWJlbCkgICANCiAgICANCiAgICAjIENvdW50IHRoZSBudW1iZXIgb2YgaXRlbXMgaW4gaXRzIGNsdXN0ZXIgICAgDQogICAgc2FtZV9jbHVzdGVyIDwtIHN1bShkYXRhc2V0JGNsdXN0ZXIgPT0gY2x1c3RlcikNCiAgICANCiAgICAjIENvdW50IHRoZSBudW1iZXIgb2YgaXRlbXMgaW4gaXRzIGNhdGVnb3J5DQogICAgdG90YWxfc2FtZV9jYXRlZ29yeSA8LSBzdW0oZGF0YXNldCRsYWJlbCA9PSBsYWJlbCkgICANCiAgICANCiAgICAjIENhbGN1bGF0ZSBwcmVjaXNpb24gYW5kIHJlY2FsbCANCiAgICBwcmVjaXNpb25fc3VtIDwtIHByZWNpc2lvbl9zdW0gKyBzYW1lX2NhdGVnb3J5IC8gc2FtZV9jbHVzdGVyDQogICAgcmVjYWxsX3N1bSA8LSByZWNhbGxfc3VtICsgc2FtZV9jYXRlZ29yeSAvIHRvdGFsX3NhbWVfY2F0ZWdvcnkgDQogICAgfQ0KICAjIEVuZCBsb29wIA0KICANCiAgIyBDYWxjdWxhdGUgYXZlcmFnZSBwcmVjaXNpb24gYW5kIHJlY2FsbCAgDQogIHByZWNpc2lvbiA8LSBwcmVjaXNpb25fc3VtIC8gbg0KICByZWNhbGwgPC0gcmVjYWxsX3N1bSAvIG4gDQogIHJldHVybihsaXN0KHByZWNpc2lvbiA9IHByZWNpc2lvbiwgcmVjYWxsID0gcmVjYWxsKSl9DQoNCiAgIyBDYWxjdWxhdGUgQkN1YmVkIHByZWNpc2lvbiBhbmQgcmVjYWxsDQogIG1ldHJpY3MgPC0gY2FsY3VsYXRlX2JjdWJlZF9tZXRyaWNzKGRhdGFzZXQpDQogIHByZWNpc2lvbiA8LSBtZXRyaWNzJHByZWNpc2lvbg0KICByZWNhbGwgPC0gbWV0cmljcyRyZWNhbGwNCg0KIyBQcmludCB0aGUgcmVzdWx0cw0KICBjYXQoIkJDdWJlZCBQcmVjaXNpb249ICIsIHByZWNpc2lvbiwgIkFORCBCQ3ViZWQgUmVjYWxsPSAiLCByZWNhbGwsICJcbiIpDQpgYGANCg0KV2hpbGUgcHJlY2lzaW9uIGhpZ2hsaWdodHMgcm9vbSBmb3IgYmV0dGVyIGFjY3VyYWN5IGluIGlkZW50aWZ5aW5nDQpzaW1pbGFyIGl0ZW1zLCB0aGUgaGlnaGVyIHJlY2FsbCBpbmRpY2F0ZXMgdGhlIGFsZ29yaXRobSdzIGNhcGFiaWxpdHkgdG8NCmNhdGNoIGEgZ29vZCBhbW91bnQgb2YgYWN0dWFsIHNpbWlsYXJpdGllcyB3aXRoaW4gY2x1c3RlcnMuDQoNCg0KDQoNCioqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1ncm91cCBpbnRvIGs9NCBjbHVzdGVycy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSoqDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoNykNCg0KIyBTcGVjaWZ5IHRoZSBudW1iZXIgb2Ygcm93cyB5b3Ugd2FudCB0byBzYW1wbGUNCm51bV9yb3dzIDwtIDEwMA0KDQojIFVzZSBzYW1wbGUgd2l0aCB0aGUgc3BlY2lmaWVkIHNlZWQNCmlkeCA8LSBzYW1wbGUoMTpkaW0oSG9iYnlfRGF0YTIpWzFdLCBudW1fcm93cykNCg0KSG9iYnlfRGF0YTMgPC0gSG9iYnlfRGF0YTJbaWR4LCBdDQoNCnBhbS5yZXN1bHQgPC0gcGFtKEhvYmJ5X0RhdGEzLDQpDQojU2hvdyB0aGUgc2lsaG91dGVlIHBsb3Qgb2YgUEFNIEFORCBjbHVzdGVycw0KcGxvdChwYW0ucmVzdWx0KQ0KYGBgDQoNCg0KYGBge3J9DQojIEV4dHJhY3QgdGhlIGNsdXNpbmZvIGNvbXBvbmVudA0KY2x1c2luZm8gPC0gcGFtLnJlc3VsdCRjbHVzaW5mbw0KDQojIENhbGN1bGF0ZSB0aGUgdG90YWwgd2l0aGluLWNsdXN0ZXIgc3VtIG9mIHNxdWFyZXMNCnRvdF93aXRoaW5zcyA8LSBzdW0oY2x1c2luZm9bLCAic2l6ZSJdICogY2x1c2luZm9bLCAiYXZfZGlzcyJdXjIpDQoNCiMgUHJpbnQgdGhlIHJlc3VsdA0KcHJpbnQodG90X3dpdGhpbnNzKQ0KYGBgDQpUaGUgb3V0cHV0IHN1Z2dlc3RzIHRoYXQgdGhlIGRhdGFzZXQgbWF5IGV4aGliaXQgYSBkZWdyZWUgb2Ygb3ZlcmxhcCBvcg0Kc2ltaWxhcml0eSBhbW9uZyBvYnNlcnZhdGlvbnMuIFRoZSBvdmVybGFwcGluZyBjbHVzdGVycyBtYXkgaW5kaWNhdGUNCmNoYWxsZW5nZXMgaW4gYWNoaWV2aW5nIGEgY2xlYXIgc2VwYXJhdGlvbiBhbW9uZyB0aGVzZSBncm91cHMuIFRoZQ0KcGxhY2VtZW50IG9mIG9uZSBjbHVzdGVyIG9uIHRvcCBvZiB0d28gb3RoZXJzIGltcGxpZXMgdGhhdCB0aGUgbWVkb2lkIG9mDQp0aGlzIGNsdXN0ZXIgbWlnaHQgYmUgbmVhciBwb2ludHMgYmVsb25naW5nIHRvIHRob3NlIHR3byBuZWlnaGJvcmluZw0KY2x1c3RlcnMuIFRoaXMgY291bGQgYmUgZHVlIHRvIHRoZSBuYXR1cmUgb2YgdGhlIGRhdGEuDQoNCmBgYHtyfQ0KY2x1c3Rlcl9hc3NpZ25tZW50cyA8LSBwYW0ucmVzdWx0JGNsdXN0ZXJpbmcNCg0KIyBBZGQgY2x1c3RlciBhc3NpZ25tZW50cyB0byB0aGUgb3JpZ2luYWwgZGF0YQ0KSG9iYnlfRGF0YV93aXRoX2NsdXN0ZXJzIDwtIGNiaW5kKEhvYmJ5X0RhdGEzLCBDbHVzdGVyID0gZmFjdG9yKGNsdXN0ZXJfYXNzaWdubWVudHMpKQ0KDQojIEV4YW1wbGU6IFBhaXIgcGxvdCBmb3IgdGhlIGZpcnN0IGZvdXIgZmVhdHVyZXMNCnBhaXJzKEhvYmJ5X0RhdGFfd2l0aF9jbHVzdGVyc1ssIDE6NF0sIGNvbCA9IGNsdXN0ZXJfYXNzaWdubWVudHMpDQoNCg0KDQpgYGANCg0KDQoNCg0KDQpCQ3ViZWQNCmBgYHtyfQ0KY2x1c3Rlcl9hc3NpZ25tZW50cyA8LSBjKHBhbS5yZXN1bHQkY2x1c3RlcikgDQogDQpzZXQuc2VlZCg3KSANCiANCiMgU3BlY2lmeSB0aGUgbnVtYmVyIG9mIHJvd3MgeW91IHdhbnQgdG8gc2FtcGxlIA0KbnVtX3Jvd3MgPC0gMTAwIA0KIA0KIyBVc2Ugc2FtcGxlIHdpdGggdGhlIHNwZWNpZmllZCBzZWVkIA0KaWR4IDwtIHNhbXBsZSgxOmRpbShIb2JieV9wcm9jKVsxXSwgbnVtX3Jvd3MpIA0KIA0KIyBTZWxlY3QgdGhlIHNhbXBsZWQgcm93cyBmcm9tIEhvYmJ5X3Byb2MgDQpIb2JieV9EYXRhNCA8LSBIb2JieV9wcm9jW2lkeCwgXSANCg0KIyBDcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggY2x1c3RlciBhc3NpZ25tZW50cyBhbmQgZ3JvdW5kIHRydXRoIGxhYmVscw0KZGF0YXNldCA8LSBkYXRhLmZyYW1lKGNsdXN0ZXIgPSBjbHVzdGVyX2Fzc2lnbm1lbnRzLCBsYWJlbCA9IGdyb3VuZF90cnV0aF9sYWJlbHMpDQoNCiMgQ2FsY3VsYXRlIEJDdWJlZCBwcmVjaXNpb24gYW5kIHJlY2FsbA0KY2FsY3VsYXRlX2JjdWJlZF9tZXRyaWNzIDwtIGZ1bmN0aW9uKGRhdGFzZXQpIHsNCiAgbiA8LSBucm93KGRhdGFzZXQpDQogIHByZWNpc2lvbl9zdW0gPC0gMA0KICByZWNhbGxfc3VtIDwtIDANCiANCiAgZm9yIChpIGluIDE6bikgew0KICAgIGNsdXN0ZXIgPC0gZGF0YXNldCRjbHVzdGVyW2ldDQogICAgbGFiZWwgPC0gZGF0YXNldCRsYWJlbFtpXQ0KICAgDQogICAgIyBDb3VudCB0aGUgbnVtYmVyIG9mIGl0ZW1zIGZyb20gdGhlIHNhbWUgY2F0ZWdvcnkgaW4gaXRzIGNsdXN0ZXINCiAgICBzYW1lX2NhdGVnb3J5IDwtIHN1bShkYXRhc2V0JGxhYmVsW2RhdGFzZXQkY2x1c3RlciA9PSBjbHVzdGVyXSA9PSBsYWJlbCkNCiAgIA0KICAgICMgQ291bnQgdGhlIG51bWJlciBvZiBpdGVtcyBpbiBpdHMgY2x1c3Rlcg0KICAgIHNhbWVfY2x1c3RlciA8LSBzdW0oZGF0YXNldCRjbHVzdGVyID09IGNsdXN0ZXIpDQogICANCiAgICAjIENvdW50IHRoZSBudW1iZXIgb2YgaXRlbXMgaW4gaXRzIGNhdGVnb3J5DQogICAgdG90YWxfc2FtZV9jYXRlZ29yeSA8LSBzdW0oZGF0YXNldCRsYWJlbCA9PSBsYWJlbCkNCiAgIA0KICAgICMgQ2FsY3VsYXRlIHByZWNpc2lvbiBhbmQgcmVjYWxsDQogICAgcHJlY2lzaW9uX3N1bSA8LSBwcmVjaXNpb25fc3VtICsgc2FtZV9jYXRlZ29yeSAvIHNhbWVfY2x1c3Rlcg0KICAgIHJlY2FsbF9zdW0gPC0gcmVjYWxsX3N1bSArIHNhbWVfY2F0ZWdvcnkgLyB0b3RhbF9zYW1lX2NhdGVnb3J5DQogIH0NCiAgIyBFbmQgbG9vcA0KIA0KICAjIENhbGN1bGF0ZSBhdmVyYWdlIHByZWNpc2lvbiBhbmQgcmVjYWxsDQogIHByZWNpc2lvbiA8LSBwcmVjaXNpb25fc3VtIC8gbg0KICByZWNhbGwgPC0gcmVjYWxsX3N1bSAvIG4NCiANCiAgcmV0dXJuKGxpc3QocHJlY2lzaW9uID0gcHJlY2lzaW9uLCByZWNhbGwgPSByZWNhbGwpKQ0KfQ0KDQojIENhbGN1bGF0ZSBCQ3ViZWQgcHJlY2lzaW9uIGFuZCByZWNhbGwNCm1ldHJpY3MgPC0gY2FsY3VsYXRlX2JjdWJlZF9tZXRyaWNzKGRhdGFzZXQpDQpwcmVjaXNpb24gPC0gbWV0cmljcyRwcmVjaXNpb24NCnJlY2FsbCA8LSBtZXRyaWNzJHJlY2FsbA0KDQojIFByaW50IHRoZSByZXN1bHRzDQpjYXQoIkJDdWJlZCBQcmVjaXNpb249ICIsIHByZWNpc2lvbiwgIkFORCBCQ3ViZWQgUmVjYWxsPSAiLCByZWNhbGwsICJcbiIpDQpgYGANCg0KVGhlIHJlc3VsdHMgaW5kaWNhdGUgY2hhbGxlbmdlcyBpbiBjbHVzdGVyaW5nIHBlcmZvcm1hbmNlLiBUaGUgbG93DQpwcmVjaXNpb24gc3VnZ2VzdHMgYSBzaWduaWZpY2FudCByYXRlIG9mIG1pc2NsYXNzaWZpY2F0aW9uLCB3aGlsZSB0aGUNCnJlbGF0aXZlbHkgbG93IHJlY2FsbCBpbmRpY2F0ZXMgdGhhdCBzb21lIGluc3RhbmNlcyB3aXRoaW4gdGhlIHNhbWUNCmdyb3VwIGFyZSBtaXNzZWQgb3IgaW5jb3JyZWN0bHkgYXNzaWduZWQgdG8gb3RoZXIgY2x1c3RlcnMuIFRoZXNlDQpyZXN1bHRzIGhpZ2hsaWdodCBsaW1pdGF0aW9ucyBpbiBhY2N1cmF0ZWx5IGNhcHR1cmluZyB0aGUgZGF0YSdzDQp1bmRlcmx5aW5nIHN0cnVjdHVyZS4NCg0KDQoNCioqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLWdyb3VwIGludG8gaz0yIGNsdXN0ZXJzLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qKg0KDQpgYGB7cn0NCnNldC5zZWVkKDcpDQoNCiMgU3BlY2lmeSB0aGUgbnVtYmVyIG9mIHJvd3MgeW91IHdhbnQgdG8gc2FtcGxlDQpudW1fcm93cyA8LSAxMDANCg0KIyBVc2Ugc2FtcGxlIHdpdGggdGhlIHNwZWNpZmllZCBzZWVkDQppZHggPC0gc2FtcGxlKDE6ZGltKEhvYmJ5X0RhdGEyKVsxXSwgbnVtX3Jvd3MpDQoNCkhvYmJ5X0RhdGEzIDwtIEhvYmJ5X0RhdGEyW2lkeCwgXQ0KDQpwYW0ucmVzdWx0IDwtIHBhbShIb2JieV9EYXRhMywyKQ0KI1Nob3cgdGhlIHNpbGhvdXRlZSBwbG90IG9mIFBBTSBBTkQgY2x1c3RlcnMNCnBsb3QocGFtLnJlc3VsdCkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBFeHRyYWN0IHRoZSBjbHVzaW5mbyBjb21wb25lbnQNCmNsdXNpbmZvIDwtIHBhbS5yZXN1bHQkY2x1c2luZm8NCg0KIyBDYWxjdWxhdGUgdGhlIHRvdGFsIHdpdGhpbi1jbHVzdGVyIHN1bSBvZiBzcXVhcmVzDQp0b3Rfd2l0aGluc3MgPC0gc3VtKGNsdXNpbmZvWywgInNpemUiXSAqIGNsdXNpbmZvWywgImF2X2Rpc3MiXV4yKQ0KDQojIFByaW50IHRoZSByZXN1bHQNCnByaW50KHRvdF93aXRoaW5zcykNCmBgYA0KQW4gb3ZlcmxhcCBiZXR3ZWVuIGNsdXN0ZXJzIGltcGxpZXMgdGhhdCB0aGVyZSBpcyBhbWJpZ3VpdHkgaW4gdGhlDQphc3NpZ25tZW50IG9mIGRhdGEgcG9pbnRzIHRvIGNsdXN0ZXJzLCBhbmQgdGhlIGNsdXN0ZXJzIG1heSBub3QgYmUNCnN1ZmZpY2llbnRseSBkaXN0aW5jdC4gSW4gc3VjaCBjYXNlcywgaXQgbWlnaHQgYmUgbmVlZGVkIHRvIHJlY29uc2lkZXINCnRoZSBudW1iZXIgb2YgY2x1c3RlcnMgc2luY2UgdGhlIGdvYWwgaXMgdG8gZmluZCBhIGJhbGFuY2UsIGJ1dCB0b28gZmV3DQpjbHVzdGVycyByZXN1bHQgaW4gb3ZlcnNpbXBsaWZpY2F0aW9uLCBhcyBpbmRpY2F0ZWQgYnkgdGhlIG9ic2VydmVkDQpvdmVybGFwLCBhbmQgaXQgaXMgZXZpZGVudCB0aGF0IGZvcm1pbmcgb25seSB0d28gY2x1c3RlcnMgbWF5IG5vdCBiZQ0Kc3VmZmljaWVudCB0byByZXByZXNlbnQgdGhlIGluaGVyZW50IHN0cnVjdHVyZSBvZiB0aGUgZGF0YXNldC4NCg0KDQojQkN1YmVkDQoNCmBgYHtyfQ0KY2x1c3Rlcl9hc3NpZ25tZW50cyA8LSBjKHBhbS5yZXN1bHQkY2x1c3RlcikgDQogDQpzZXQuc2VlZCg3KSANCiANCiMgU3BlY2lmeSB0aGUgbnVtYmVyIG9mIHJvd3MgeW91IHdhbnQgdG8gc2FtcGxlIA0KbnVtX3Jvd3MgPC0gMTAwIA0KIA0KIyBVc2Ugc2FtcGxlIHdpdGggdGhlIHNwZWNpZmllZCBzZWVkIA0KaWR4IDwtIHNhbXBsZSgxOmRpbShIb2JieV9wcm9jKVsxXSwgbnVtX3Jvd3MpIA0KIA0KIyBTZWxlY3QgdGhlIHNhbXBsZWQgcm93cyBmcm9tIEhvYmJ5X3Byb2MgDQpIb2JieV9EYXRhNCA8LSBIb2JieV9wcm9jW2lkeCwgXSANCg0KIyBDcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggY2x1c3RlciBhc3NpZ25tZW50cyBhbmQgZ3JvdW5kIHRydXRoIGxhYmVscw0KZGF0YXNldCA8LSBkYXRhLmZyYW1lKGNsdXN0ZXIgPSBjbHVzdGVyX2Fzc2lnbm1lbnRzLCBsYWJlbCA9IGdyb3VuZF90cnV0aF9sYWJlbHMpDQoNCiMgQ2FsY3VsYXRlIEJDdWJlZCBwcmVjaXNpb24gYW5kIHJlY2FsbA0KY2FsY3VsYXRlX2JjdWJlZF9tZXRyaWNzIDwtIGZ1bmN0aW9uKGRhdGFzZXQpIHsNCiAgbiA8LSBucm93KGRhdGFzZXQpDQogIHByZWNpc2lvbl9zdW0gPC0gMA0KICByZWNhbGxfc3VtIDwtIDANCiANCiAgZm9yIChpIGluIDE6bikgew0KICAgIGNsdXN0ZXIgPC0gZGF0YXNldCRjbHVzdGVyW2ldDQogICAgbGFiZWwgPC0gZGF0YXNldCRsYWJlbFtpXQ0KICAgDQogICAgIyBDb3VudCB0aGUgbnVtYmVyIG9mIGl0ZW1zIGZyb20gdGhlIHNhbWUgY2F0ZWdvcnkgaW4gaXRzIGNsdXN0ZXINCiAgICBzYW1lX2NhdGVnb3J5IDwtIHN1bShkYXRhc2V0JGxhYmVsW2RhdGFzZXQkY2x1c3RlciA9PSBjbHVzdGVyXSA9PSBsYWJlbCkNCiAgIA0KICAgICMgQ291bnQgdGhlIG51bWJlciBvZiBpdGVtcyBpbiBpdHMgY2x1c3Rlcg0KICAgIHNhbWVfY2x1c3RlciA8LSBzdW0oZGF0YXNldCRjbHVzdGVyID09IGNsdXN0ZXIpDQogICANCiAgICAjIENvdW50IHRoZSBudW1iZXIgb2YgaXRlbXMgaW4gaXRzIGNhdGVnb3J5DQogICAgdG90YWxfc2FtZV9jYXRlZ29yeSA8LSBzdW0oZGF0YXNldCRsYWJlbCA9PSBsYWJlbCkNCiAgIA0KICAgICMgQ2FsY3VsYXRlIHByZWNpc2lvbiBhbmQgcmVjYWxsDQogICAgcHJlY2lzaW9uX3N1bSA8LSBwcmVjaXNpb25fc3VtICsgc2FtZV9jYXRlZ29yeSAvIHNhbWVfY2x1c3Rlcg0KICAgIHJlY2FsbF9zdW0gPC0gcmVjYWxsX3N1bSArIHNhbWVfY2F0ZWdvcnkgLyB0b3RhbF9zYW1lX2NhdGVnb3J5DQogIH0NCiAgIyBFbmQgbG9vcA0KIA0KICAjIENhbGN1bGF0ZSBhdmVyYWdlIHByZWNpc2lvbiBhbmQgcmVjYWxsDQogIHByZWNpc2lvbiA8LSBwcmVjaXNpb25fc3VtIC8gbg0KICByZWNhbGwgPC0gcmVjYWxsX3N1bSAvIG4NCiANCiAgcmV0dXJuKGxpc3QocHJlY2lzaW9uID0gcHJlY2lzaW9uLCByZWNhbGwgPSByZWNhbGwpKQ0KfQ0KDQojIENhbGN1bGF0ZSBCQ3ViZWQgcHJlY2lzaW9uIGFuZCByZWNhbGwNCm1ldHJpY3MgPC0gY2FsY3VsYXRlX2JjdWJlZF9tZXRyaWNzKGRhdGFzZXQpDQpwcmVjaXNpb24gPC0gbWV0cmljcyRwcmVjaXNpb24NCnJlY2FsbCA8LSBtZXRyaWNzJHJlY2FsbA0KDQojIFByaW50IHRoZSByZXN1bHRzDQpjYXQoIkJDdWJlZCBQcmVjaXNpb249ICIsIHByZWNpc2lvbiwgIkFORCBCQ3ViZWQgUmVjYWxsPSAiLCByZWNhbGwsICJcbiIpDQpgYGANCg0KVGhlIHJlbGF0aXZlbHkgaGlnaCByZWNhbGwgY291bGQgYmUgaW5mbHVlbmNlZCBieSB0aGUgc3BlY2lmaWMgY2hvaWNlIG9mDQp0d28gY2x1c3RlcnMgc2luY2UgaXQgaXMgc2Vuc2l0aXZlIHRvIHRoZSBudW1iZXIgb2YgY2x1c3RlcnMuIEluIHRoZQ0KY29udGV4dCBvZiBhIHR3by1jbHVzdGVyIHNvbHV0aW9uLCB0aGUgcmVjYWxsIHNjb3JlIHJlZmxlY3RzIGhvdyB3ZWxsDQp0aGUgYWxnb3JpdGhtIGdyb3VwcyBkYXRhIHBvaW50cyBmcm9tIHRoZSBzYW1lIGNsYXNzIGludG8gb25lIG9mIHRoZSB0d28NCmlkZW50aWZpZWQgY2x1c3RlcnMuIFJlY2FsbCBzdWdnZXN0cyB0aGF0IGEgc2lnbmlmaWNhbnQgcG9ydGlvbiBvZiBkYXRhDQpwb2ludHMgZnJvbSB0aGUgc2FtZSBncm91bmQgdHJ1dGggY2xhc3MgYXJlIGluZGVlZCBncm91cGVkIHRvZ2V0aGVyIGluDQpvbmUgb2YgdGhlIHR3byBjbHVzdGVycy4gSG93ZXZlciwgaXQncyBpbXBvcnRhbnQgdG8gbm90ZSB0aGF0IHRoZSBsb3cNCnByZWNpc2lvbiBzY29yZSAoMC4wNDMyKSBpbmRpY2F0ZXMgYSBsYWNrIG9mIGhvbW9nZW5laXR5IHdpdGhpbiB0aGUNCmlkZW50aWZpZWQgY2x1c3RlcnMsIGltcGx5aW5nIHRoYXQgdGhlIGNsdXN0ZXJzIGNvbnRhaW4gYSBtaXggb2YgZGF0YQ0KcG9pbnRzIGZyb20gZGlmZmVyZW50IGdyb3VuZCB0cnV0aCBjbGFzc2VzLg0KDQoNCg0KDQoNCg0KDQoqKkFTIEEgU3VtbWFyeSBGb3IgY2x1c3RyaW5nKioNClNpbGhvdWV0dGUgYW5hbHlzaXMgbWVhc3VyZXMgaG93IHNpbWlsYXIgYW4gb2JqZWN0IGlzIHRvIGl0cyBvd24gY2x1c3Rlcg0KKGNvaGVzaW9uKSBjb21wYXJlZCB0byBvdGhlciBjbHVzdGVycyAoc2VwYXJhdGlvbikuIFRoZSBzaWxob3VldHRlIHdpZHRoDQpyYW5nZXMgZnJvbSAtMSB0byAxLCB3aGVyZSBhIGhpZ2ggdmFsdWUgaW5kaWNhdGVzIHRoYXQgdGhlIG9iamVjdCBpcw0Kd2VsbCBtYXRjaGVkIHRvIGl0cyBvd24gY2x1c3RlciBhbmQgcG9vcmx5IG1hdGNoZWQgdG8gbmVpZ2hib3JpbmcNCmNsdXN0ZXJzLiBJbiBvdXIgcHJvamVjdCB3ZSBkaWQgY2x1c3RlcnMgZm9yIGs9Mywgaz00LCBhbmQgaz0yIHNpbmNlIGl0cw0KaGF2ZSBoaWdoZXIgYXZlcmFnZSBzaWxob3VldHRlIHRoZW4gb3RoZXIgbnVtYmVyLg0KDQpGb3Igaz0zLCB0aGUgYXZlcmFnZSBzaWxob3VldHRlIHdpZHRoIGlzIDAuMjIuIA0KRm9yIGs9NCwgdGhlIGF2ZXJhZ2Ugc2lsaG91ZXR0ZSB3aWR0aCBpcyAwLjE5LiANCkZvciBrPTIsIHRoZSBhdmVyYWdlIHNpbGhvdWV0dGUgd2lkdGggaXMgMC4yMS4NCg0KQSBoaWdoZXIgYXZlcmFnZSBzaWxob3VldHRlIHdpZHRoIGdlbmVyYWxseSBpbmRpY2F0ZXMgYmV0dGVyLWRlZmluZWQNCmNsdXN0ZXJzLiBTbywgaW4gdGhpcyBjYXNlLCBrPTMgaGFzIHRoZSBoaWdoZXN0IGF2ZXJhZ2Ugc2lsaG91ZXR0ZQ0Kd2lkdGguDQoNCkJDdWJlZCBpcyBhIGNsdXN0ZXJpbmcgZXZhbHVhdGlvbiBtZXRyaWMgdGhhdCBjb25zaWRlcnMgYm90aCBwcmVjaXNpb24NCmFuZCByZWNhbGwuIFByZWNpc2lvbiBtZWFzdXJlcyB0aGUgYWNjdXJhY3kgb2YgdGhlIHBvc2l0aXZlIHByZWRpY3Rpb25zLA0Kd2hpbGUgcmVjYWxsIG1lYXN1cmVzIHRoZSBjb3ZlcmFnZSBvZiB0aGUgYWN0dWFsIHBvc2l0aXZlIGluc3RhbmNlcy4NCg0KRm9yIGs9MywgQkJDdWJlZCBQcmVjaXNpb24gPSAwLjA0ODggQkN1YmVkIFJlY2FsbCA9IDAuNDkzMg0KRm9yIGs9NCxCQ3ViZWQgUHJlY2lzaW9uID0gMC4wNTA1IEJDdWJlZCBSZWNhbGwgPSAwLjQxMDENCkZvciBrPTIsIEJDdWJlZFByZWNpc2lvbiA9IDAuMDQzMiBCQ3ViZWQgUmVjYWxsID0gMC42MjUyDQoNClRoZXNlIG1ldHJpY3MgbWVhc3VyZSBob3cgd2VsbCB0aGUgY2x1c3RlcmluZyBhbGlnbnMgd2l0aCB0aGUgZ3JvdW5kDQp0cnV0aC4gSGlnaGVyIHByZWNpc2lvbiBpbmRpY2F0ZXMgZmV3ZXIgZmFsc2UgcG9zaXRpdmVzLCBhbmQgaGlnaGVyDQpyZWNhbGwgaW5kaWNhdGVzIGZld2VyIGZhbHNlIG5lZ2F0aXZlcy4gSGVyZSwgaz0yIGhhcyB0aGUgaGlnaGVzdCByZWNhbGwNCigwLjYyNTIpLCBidXQgaz0zIGhhcyBhIHJlYXNvbmFibGUgYmFsYW5jZSBiZXR3ZWVuIHByZWNpc2lvbiBhbmQgcmVjYWxsLg0KDQpJbiBjb25jbHVzaW9uLCBrPTMgc2VlbXMgdG8gYmUgYSByZWFzb25hYmxlIGNob2ljZS4gSXQgaGFzIGEgZ29vZA0Kc2lsaG91ZXR0ZSB3aWR0aCwgYW5kIGl0cyBCQ3ViZWQgUHJlY2lzaW9uIGFuZCBSZWNhbGwgdmFsdWVzIHN0cmlrZSBhDQpiYWxhbmNlLg0KDQoNCmBgYHtyfQ0KbGlicmFyeShHR2FsbHkpDQpsaWJyYXJ5KHBsb3RseSkNCg0KSG9iYnlfRGF0YTIkY2x1c3RyaW5nIDwtIGFzLmZhY3RvcihIb2JieV9EYXRhMiRjbHVzdGVyKQ0KDQpwPC0gZ2dwYXJjb29yZCAoZGF0YT1Ib2JieV9EYXRhMiwgY29sdW1ucyA9IGMoMToxMSksIGdyb3VwQ29sdW1uID0iY2x1c3RlciIsIHNjYWxlID0gInN0ZCIpKyBsYWJzKHggPSAiIiwgeT0iIiksIHRpdGxlID0gIlBhcmFsbGVsIENvb3JkaW5hdGUgUGxvdCIpDQpnZ3Bsb3RseShwKQ0KYGBgDQoNCg0KDQoNCioqRmluZGluZ3MqKg0KDQoqKlJlZmVyZW5jZXMqKg0KDQoNCioqV2hhdCBpcyB0aGUgYmVzdCBmb3Igb3VyIGRhdGFzZXQ/KioNCg0Kc2luY2UgV2UgdXNlZCB0aGUgSW5mb3JtYXRpb24gR2FpbiwgR2luaSBJbmRleCwgYW5kIEdhaW4gUmF0aW8gYXMgdGhyZWUgaW1wb3J0YW50IG1ldHJpY3MgaW4gb3VyIGNhdGVnb3JpY2FsIGRhdGEgY2xhc3NpZmljYXRpb24gYXBwcm9hY2ggdG8gZGV0ZXJtaW5lIGhvdyBpbXBvcnRhbnQgY2VydGFpbiB2YXJpYWJsZXMgd2VyZSBpbiBwcmVkaWN0aW5nIGhvYmJ5IGNhdGVnb3JpZXMuIFRoZSBkYXRhc2V0IHdhcyBzcGxpdCBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldHMgc28gdGhhdCBkaWZmZXJlbnQgc3Vic2V0cyBvZiB0aGUgZGF0YSBjb3VsZCBiZSB1c2VkIHRvIHRyYWluIGNsYXNzaWZpY2F0aW9uIGFsZ29yaXRobXMuIFRocmVlIG1ldHJpY3Mgd2VyZSB1c2VkIHRvIGV2YWx1YXRlIHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgbW9kZWw6IG92ZXJhbGwgYWNjdXJhY3ksIHJlY2FsbCwgYW5kIHByZWNpc2lvbi5XZSBleGFtaW5lZCBhY2N1cmFjeSBhdCBzZXZlcmFsIG1lYXN1cmVzIG9mIHRyZWUgc2VsZWN0aW9uLCBQcm92aWRlIHVzIHdpdGggbW9yZSBhY2N1cmF0ZSByZXN1bHRzLCBzcGVjaWZpY2FsbHkgYXQgdGhlIHBydW5pbmcgcGFyYW1ldGVycyBvZiAwLjkwLCAwLjkxLCBhbmQgMC44OSwgaW4gb3JkZXIuVGhlIG1ldGhvZGljYWwgZXZhbHVhdGlvbiB5aWVsZGVkIGluc2lnaHRmdWwgaW5mb3JtYXRpb24gb24gaG93IHdlbGwgZWFjaCBzZWxlY3Rpb24gbWVhc3VyZSBwZXJmb3JtZWQgaW4gcHJvZHVjaW5nIHByZWNpc2UgcHJlZGljdGlvbnMgd2l0aGluIHRoZSBkYXRhc2V0LiBoaWdoIGFjY3VyYWN5IHN0cm9uZ2x5IHN1Z2dlc3QgdGhhdCwgZm9yIG91ciBkYXRhc2V0LCBjbGFzc2lmaWNhdGlvbiBpcyBhIGJldHRlciBtZXRob2RvbG9neSB0aGFuIGNsdXN0ZXJpbmcsIHNpbmNlIHRoZSBjbGFzc2lmaWNhdGlvbiBnaXZlIHVzIGEgZ29vZCByZXN1bHQgdGhlbiBjbHVzdHJpbmcuDQoNCg==